Background
It's possible to change the background of the actionbar, and even animate between two colors, as such:
public static void animateBetweenColors(final ActionBar actionBar, final int colorFrom, final int colorTo,
final int durationInMs) {
final ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {
ColorDrawable colorDrawable = new ColorDrawable(colorFrom);
#Override
public void onAnimationUpdate(final ValueAnimator animator) {
colorDrawable.setColor((Integer) animator.getAnimatedValue());
actionBar.setBackgroundDrawable(colorDrawable);
}
});
if (durationInMs >= 0)
colorAnimation.setDuration(durationInMs);
colorAnimation.start();
}
The problem
I can't find a way to get the view of the action mode, so that I could change its background on some cases (while it's showing).
What I tried
Only thing I found is a hack-y way which assumes that the id of the action mode will stay the same, and even this would work just for the view of the "done" button (the one that looks like an "V" and is actually more like "cancel").
I also found how to change it via themes, but that's not what I need, since I need to do it programmatically.
The question
How do I get the view of the actionMode, or, more precisely, how can I change its background using an animation?
How do I get the view of the actionMode, or, more precisely, how can I
change its background using an animation?
You have two choices, unfortunately neither of which involve native ActionMode APIs:
The ActionBarContextView is responsible for controlling the ActionMode
Use Resources.getIdentifier to call Activity.findViewById and pass in the ID the system uses for the ActionBarContextView
Use reflection to access to Field in ActionBarImpl
Here's an example of both:
Using Resources.getIdentifier:
private void animateActionModeViaFindViewById(int colorFrom, int colorTo, int duration) {
final int amId = getResources().getIdentifier("action_context_bar", "id", "android");
animateActionMode(findViewById(amId), colorFrom, colorTo, duration);
}
Using reflection:
private void animateActionModeViaReflection(int colorFrom, int colorTo, int duration) {
final ActionBar actionBar = getActionBar();
try {
final Field contextView = actionBar.getClass().getDeclaredField("mContextView");
animateActionMode((View) contextView.get(actionBar), colorFrom, colorTo, duration);
} catch (final Exception ignored) {
// Nothing to do
}
}
private void animateActionMode(final View actionMode, final int from, int to, int duration) {
final ValueAnimator va = ValueAnimator.ofObject(new ArgbEvaluator(), from, to);
final ColorDrawable actionModeBackground = new ColorDrawable(from);
va.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(final ValueAnimator animator) {
actionModeBackground.setColor((Integer) animator.getAnimatedValue());
actionMode.setBackground(actionModeBackground);
}
});
va.setDuration(duration);
va.start();
}
Results
Here's a gif of the results animating from Color.BLACK to Color.BLUE at a duration of 2500:
Related
I have a LinearLayout with an initial background (lets say : #ff4455) and I want the background color to change to another value when clicking a buttonin Smooth way (FADE) .. How to do that ..
NOTE : I have FOUR buttons, each button will change the background to another color..
Use a value animator
int[] colors = new int[]{...} // create an array with colors for each button
button1.setOnClickListener(v -> updateBackgroundColor(0));
button2.setOnClickListener(v -> updateBackgroundColor(1));
button3.setOnClickListener(v -> updateBackgroundColor(2));
button4.setOnClickListener(v -> updateBackgroundColor(3));
private void updateBackgroundColor(int buttonPos) {
Drawable background = view.getBackground();
if (background instanceof ColorDrawable)
color = ((ColorDrawable) background).getColor();
int colorFrom = color == null ? defaultColor : color;
int colorTo = colors[buttonPos];
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.setDuration(250); // milliseconds
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animator) {
linearLayout.setBackgroundColor((int) animator.getAnimatedValue());
}
});
colorAnimation.start();
}
I created a RecyclerView which I want to animate by clicking on the CardView inside it. I want to apply this animation of material design which resize the selected item and push up the item below and push down the item above :
I tried with this code but it just collapses and expands without pushing the item below :
private void expandCardView()
{
mCardViewContent.setVisibility(View.VISIBLE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
mCardViewContent.measure(widthSpec, heightSpec);
ValueAnimator mAnimator = slideAnimator(0, mCardViewContent.getMeasuredHeight());
mAnimator.start();
}
private void collapseCardView ()
{
int finalHeight = mCardViewContent.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
mAnimator.addListener(new AnimatorListenerAdapter()
{
#Override
public void onAnimationEnd(Animator animator)
{
mCardViewContent.setVisibility(View.GONE);
}
});
mAnimator.start();
}
private ValueAnimator slideAnimator(int start, int end)
{
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(valueAnimator->
{
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = mCardViewContent.getLayoutParams();
layoutParams.height = value;
mCardViewContent.setLayoutParams(layoutParams);
});
return animator;
}
I had faced exactly same problem in my few projects and I have found a great solution at stackoverflow, which works like charm!
https://stackoverflow.com/a/13381228/6569739
You can make an invisible RelativeLayout in each RecyclerView element, which will hold all the data You want the user to see when expaned.
Then simply call expand(View v) on specific layout to expand it.
For now that is the best and most reliable way to make this kind of animation I've found so far!
I'm trying to change the background color of my view. I wrote some code and i can change color with animation, but first time my view is freezing before color changes. This is my code
private void changeBackgroundColorWithAnimation(int duration, final View view, int startColor, int endColor) {
ValueAnimator anim = new ValueAnimator();
anim.setIntValues(startColor, endColor);
anim.setEvaluator(new ArgbEvaluator());
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(final ValueAnimator valueAnimator) {
view.setBackgroundColor((Integer) valueAnimator.getAnimatedValue());
}
});
anim.setDuration(duration);
anim.start();
}
I call my function like this:
changeBackgroundColorWithAnimation(300, TransferFragmentNewVersion.rootLayout,
Color.parseColor("#E6000000"), Color.WHITE);
As I said the background color has changed, but first time View is freezing(only first time)l
How can I solve my problem? Thanks everyone.
Why aren't you sending your view's initial color as a starting color?
You can replace Color.parseColor("#E6000000") with TransferFragmentNewVersion.rootLayout.getSolidColor().
Can you post more of your code?
In general, I'm trying to animate on an array of int values, unlike the typical case where you are just animating an int or float from one number to another.
To be specific, I'm trying to animate the int [] of colors on a GradientDrawable object.
GradientDrawable has a property named "setColors(int []) " in which I set 2 colors, the starting color and ending color, which make up a whole gradient.
I want to animate from one combination of colors towards another. If it were a solid color, I could already do this, like the following:
Integer colorFrom = getResources().getColor(R.color.red);
Integer colorTo = getResources().getColor(R.color.blue);
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animator) {
textView.setBackgroundColor((Integer)animator.getAnimatedValue());
}
});
colorAnimation.start();
So, I need something like this:
//Define 2 starting colors
Integer colorFrom1 = getResources().getColor(R.color.red);
Integer colorFrom2 = getResources().getColor(R.color.darkred);
int[] startColors = new int[] {colorFrom1, colorFrom2};
//Define 2 ending colors
Integer colorTo1 = getResources().getColor(R.color.blue);
Integer colorTo2 = getResources().getColor(R.color.darkblue);
int[] endColors = new int[] {colorTo1, colorTo2};
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), startColors, endColors);
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animator) {
gradientView.setColors((Integer)animator.getAnimatedValues()[0] , (Integer)animator.getAnimatedValues()[1]);
}
});
colorAnimation.start();
Obvioulsy that code won't exist because there is no getAnimatedValues() method returning an array and furthermore there is no ValueAnimator.ofObject method which accepts an array as a start and end values.
Any ideas?
My only idea now is to run two animators in parallel, each animating one dimension of the gradient, and each setting only half of the array accepted by gradientDrawable.setColors().... but boyyyy that would be nearly unacceptably inefficient and possibly dangerously out-of-sync.
How about this?
public class ArgbArrayEvaluator implements TypeEvaluator<Integer[]> {
ArgbEvaluator evaluator = new ArgbEvaluator();
public Integer[] evaluate(float fraction, Integer[] startValues, Integer[] endValues) {
if(startValues.length != endValues.length) throw new ArrayIndexOutOfBoundsException();
Integer[] values = new Integer[startValues.length];
for(int = 0;i<startValues.length;i++) {
values[i] = (Integer) evaluator.evaluate(fraction,startValues[i],endValues[i]);
}
return values;
}
}
Then do
/* Make sure startColors and endColors are Integer[] not int[] */
final ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbArrayEvaluator(),startColors,endColors);
Your listener code:
public void onAnimationUpdate(ValueAnimator animator) {
Integer[] values = (Integer[]) animator.getAnimatedValue();
gradientView.setColors(values[0],values[1]);
}
Alternatively, with ObjectAnimator
final ObjectAnimator colorAnimation = ObjectAnimator.ofMultiInt(gradientView,"colors",null, new ArgbArrayEvaluator(), startColors,endColors);
Also, in the ValueAnimator documentation for the start() method it says:
The animation started by calling this method will be run on the thread that called this method. This thread should have a Looper on it (a runtime exception will be thrown if this is not the case). Also, if the animation will animate properties of objects in the view hierarchy, then the calling thread should be the UI thread for that view hierarchy.
So I would use the following if you're not already in the UI thread.
runOnUiThread(new Runnable() {
public void run() {
colorAnimation.start();
}
});
I think it'll work!
is there a way to apply a color to an alpha animation in android? I know how to use the <alpha> element, but i'd like to have the alpha apply a color as well as an alpha so i can hightlight a layout. is this possible?
Animations can not include color changes in Android--only alpha, rotation, scale, and translation are included.
That said, you can still make color changes by overlaying two objects of different colors on top of each other and fading the top one in or out.
You could also look into a TransitionDrawable or TextSwitcher to accomplish something similar. Hopefully we will be able to get full support for color animations in a future update.
Well, here is my solution animating a particular region of the screen (see demo down below). Please note that this code is targeting devices which run >= API9.
Beginner friendly, just copy and paste.
FadeAnimationColored.java
public class FadeAnimationColored {
private View view;
private float maxBrightness = 1.0f;
private float minBrightness = 0.0f;
private long duration = 400L;
private long startOffset = 0L;
private int color = android.R.color.white;
// Constructors...
public FadeAnimationColored(View view, String color, float maxBrightness, float minBrightness, long duration, long startOffset) {
this.view = view;
this.color = Color.parseColor(color);
this.maxBrightness = maxBrightness;
this.minBrightness = minBrightness;
this.duration = duration;
this.startOffset = startOffset;
prepareView();
}
public void fadeOut() {
this.view.setAlpha(1f);
Animation anim = new AlphaAnimation(minBrightness, maxBrightness);
anim.setDuration(duration);
anim.setStartOffset(startOffset);
anim.setFillEnabled(true);
anim.setFillAfter(true);
view.startAnimation(anim);
}
public void fadeIn() {
Animation anim = new AlphaAnimation(maxBrightness, minBrightness);
anim.setDuration(duration);
anim.setStartOffset(startOffset);
anim.setFillEnabled(true);
anim.setFillAfter(true);
view.startAnimation(anim);
}
private void prepareView() {
this.view.setBackgroundColor(this.color);
this.view.setAlpha(0f);
}
}
Next add an additional View to your layout, think of it as an overlay (I used a simple FrameLayout which is set to match_parent)
Here is a snippet which shows how to set up the animation in your Activity or Fragment:
FrameLayout interceptorFrame = (FrameLayout) mView.findViewById(R.id.fl_interceptor);
final FadeAnimationColored fadeAnimationLayout =
new FadeAnimationColored(interceptorFrame, MaterialColor.GREY_800, 0.9f, 0.0f, 400L, 0);
mFabMenu.setOnMenuToggleListener(new FloatingActionMenu.OnMenuToggleListener() {
#Override
public void onMenuToggle(boolean opened) {
if (opened) {
fadeAnimationLayout.fadeOut();
} else {
fadeAnimationLayout.fadeIn();
}
}
});