Reputation: 3267
Assuming I have two colors, and I need to create a real time animation that fastly switches from a color to another.
I tried just to increment the color hexadecimal until I reach the other, but that gave a really bad animation as it showed lots of unrelated colors.
I am using setColorFilter(color, colorfilter)
to change the color of an imageview.
Changing the HUE will give me the best visual results? If so, how can I change it for a solid color?
SOLUTION: I solved it by recursively shifting hue
private int hueChange(int c,int deg){
float[] hsv = new float[3]; //array to store HSV values
Color.colorToHSV(c,hsv); //get original HSV values of pixel
hsv[0]=hsv[0]+deg; //add the shift to the HUE of HSV array
hsv[0]=hsv[0]%360; //confines hue to values:[0,360]
return Color.HSVToColor(Color.alpha(c),hsv);
}
Upvotes: 54
Views: 67940
Reputation: 1143
If you like me are trying to change from one RGB color to either white or black, you will find it difficult using @bcorso's answer since it's the wrong color space for that particular job. Not that the answer is wrong, that is not what I am saying.
Here is an example of doing the blending using a color colorA
, using white (which can be substituted with black):
float[] from = new float[3];
float[] hsl = new float[3];
ColorUtils.colorToHSL(colorA, from);
float[] to = new float[] {from[0], from[1], 1.0f /* WHITE */};
animator = new ValueAnimator();
animator.setFloatValues(0.0f, 1.0f);
animator.setDuration(1000L);
animator.addUpdateListener(animation -> {
ColorUtils.blendHSL(from, to, (float) animation.getAnimatedValue(), hsl);
setColor(ColorUtils.HSLToColor(hsl));
});
There seem to be a lot of confusion about the two, so here is a picture describing it visually.
Upvotes: 4
Reputation: 12222
best way use blendARGB in ColorUtils.
esay and minimum line code.
view.setBackgroundColor(ColorUtils.blendARGB(Color.parseColor("#FFFFFF"), Color.parseColor("#CCCCCC"), fraction));
Upvotes: 14
Reputation: 3672
I've found that the implementation used by ArgbEvaluator
in the android source code does best job in transitioning colors. When using HSV, depending on the two colors, the transition was jumping through too many hues for me. But this methods doesn't. If you are trying to simply animate use ArgbEvaluator
with ValueAnimator
as suggested here:
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
view.setBackgroundColor((int) animator.getAnimatedValue());
}
});
colorAnimation.start();
However, if you are like me and want to tie your transition with some user gesture or other value passed from an input, the ValueAnimator
is not of much help (unless your are targeting for API 22 and above, in which case you can use the ValueAnimator.setCurrentFraction()
method). When targeting below API 22, wrap the code found in ArgbEvaluator
source code in your own method, as shown below:
public static int interpolateColor(float fraction, int startValue, int endValue) {
int startA = (startValue >> 24) & 0xff;
int startR = (startValue >> 16) & 0xff;
int startG = (startValue >> 8) & 0xff;
int startB = startValue & 0xff;
int endA = (endValue >> 24) & 0xff;
int endR = (endValue >> 16) & 0xff;
int endG = (endValue >> 8) & 0xff;
int endB = endValue & 0xff;
return ((startA + (int) (fraction * (endA - startA))) << 24) |
((startR + (int) (fraction * (endR - startR))) << 16) |
((startG + (int) (fraction * (endG - startG))) << 8) |
((startB + (int) (fraction * (endB - startB))));
}
and use it however you wish.
Upvotes: 4
Reputation: 356
I tried @bcorso and @damienix and their solution didn't work for me. The math in this function is much simpler as it follows the basic interpolation formula:
p(t) = (1 - t) * p1 + t * p2, where t [0, 1]
public int interpolateColorFromTo(int currColor, int nextColor, float t) {
final float[] from = new float[3];
final float[] to = new float[3];
Color.colorToHSV(currColor, from);
Color.colorToHSV(nextColor, to);
final float[] hsv = new float[3];
hsv[0] = (1.0f - t) * from[0] + t * to[0];
hsv[1] = (1.0f - t) * from[1] + t * to[1];
hsv[2] = (1.0f - t) * from[2] + t * to[2];
return Color.HSVToColor(hsv);
}
Upvotes: 1
Reputation: 9207
You can simply use ArgbEvaluator
which is available since API 11 (Honeycomb):
ValueAnimator anim = new ValueAnimator();
anim.setIntValues(color1, color2);
anim.setEvaluator(new ArgbEvaluator());
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
view.setBackgroundColor((Integer)valueAnimator.getAnimatedValue());
}
});
anim.setDuration(300);
anim.start();
Even better, beginning with API 21 (Lollipop 5.0) you can replace the first 3 lines in the code above with one:
ValueAnimator anim = ValueAnimator.ofArgb(color1, color2)
Upvotes: 67
Reputation: 2581
The following snippet worked perfectly for me. I thought of using ValueAnimator.ofArgb() but that requires a minimum of api 21. Instead the following works with api 11 and higher. It achieves the color changing smoothly.
ArgbEvaluator evaluator = new ArgbEvaluator();
ValueAnimator animator = new ValueAnimator();
animator.setIntValues(Color.parseColor("#FFFFFF"), Color.parseColor("#000000"));
animator.setEvaluator(evaluator);
animator.setDuration(1000);
//animator.setRepeatCount(ValueAnimator.INFINITE);
//animator.setRepeatMode(ValueAnimator.REVERSE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
color = (int) animation.getAnimatedValue();
//postInvalidate(); if you are animating a canvas
//View.setBackgroundColor(color); another exampleof where to use
}
});
animator.start();
Upvotes: 5
Reputation: 3200
First, I would like to thank @bcorso for the great answer and data about HSV.
While I was using the proposed algorithm for some time, I was slightly annoyed by the fact that it makes a color shift from one color to another touching unrelated colors in the process. So, I finally implemented my own stupid simple algorithm that uses real 3D coordinates to make such interpolation:
https://github.com/konmik/animated-color
Upvotes: 0
Reputation: 47078
Combining @zed's and @Phil's answer gives a nice smooth transition using the ValueAnimator
.
final float[] from = new float[3],
to = new float[3];
Color.colorToHSV(Color.parseColor("#FFFFFFFF"), from); // from white
Color.colorToHSV(Color.parseColor("#FFFF0000"), to); // to red
ValueAnimator anim = ValueAnimator.ofFloat(0, 1); // animate from 0 to 1
anim.setDuration(300); // for 300 ms
final float[] hsv = new float[3]; // transition color
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
@Override public void onAnimationUpdate(ValueAnimator animation) {
// Transition along each axis of HSV (hue, saturation, value)
hsv[0] = from[0] + (to[0] - from[0])*animation.getAnimatedFraction();
hsv[1] = from[1] + (to[1] - from[1])*animation.getAnimatedFraction();
hsv[2] = from[2] + (to[2] - from[2])*animation.getAnimatedFraction();
view.setBackgroundColor(Color.HSVToColor(hsv));
}
});
anim.start();
The HSV will give a nicer transition than Androids default color space because HSV describes colors in cylindrical coordinates that nicely separate the color's properties and allow a smooth transition across a single axis. You can see from the image below that traveling along the H, S, or V directions gives a nice continuous transition between colors.
Upvotes: 77
Reputation: 1449
The other answers gave me a weird effect when transitioning very different colors. From yellow to gray it would reach green at some point during the animation.
What worked best for me was the following snippet. This created a really smooth transition with no weird colors appearing in-between.
private void changeViewColor(View view) {
// Load initial and final colors.
final int initialColor = getResources().getColor(R.color.some_color);
final int finalColor = getResources().getColor(R.color.another_color);
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// Use animation position to blend colors.
float position = animation.getAnimatedFraction();
int blended = blendColors(initialColor, finalColor, position);
// Apply blended color to the view.
view.setBackgroundColor(blended);
}
});
anim.setDuration(500).start();
}
private int blendColors(int from, int to, float ratio) {
final float inverseRatio = 1f - ratio;
final float r = Color.red(to) * ratio + Color.red(from) * inverseRatio;
final float g = Color.green(to) * ratio + Color.green(from) * inverseRatio;
final float b = Color.blue(to) * ratio + Color.blue(from) * inverseRatio;
return Color.rgb((int) r, (int) g, (int) b);
}
Upvotes: 12
Reputation: 36289
You can use a ValueAnimator
:
//animate from your current color to red
ValueAnimator anim = ValueAnimator.ofInt(view.getBackgroundColor(), Color.parseColor("#FF0000"));
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
view.setBackgroundColor(animation.getAnimatedValue());
}
});
anim.start();
You can also set duration or other parameters before calling anim.start()
. For example:
anim.setDuration(400);
will set the animation duration to 400ms.
Finally, note that ValueAnimator
is available starting in Honeycomb, so if you are supporting older SDKs, you can use NineOldAndroids.
Upvotes: 20
Reputation: 21097
You can use TransitionDrawable
TransitionDrawable transition = (TransitionDrawable) viewObj.getBackground();
transition.startTransition(transitionTime);
create xml file in the drawable folder you could write something like:
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/end_color" />
<item android:drawable="@drawable/start_color" />
</transition>
Then, in your xml for the actual View you would reference this TransitionDrawable in the android:background attribute.
Upvotes: 8
Reputation: 3267
SOLUTION: I solved it by recursively shifting hue
private int hueChange(int c,int deg){
float[] hsv = new float[3]; //array to store HSV values
Color.colorToHSV(c,hsv); //get original HSV values of pixel
hsv[0]=hsv[0]+deg; //add the shift to the HUE of HSV array
hsv[0]=hsv[0]%360; //confines hue to values:[0,360]
return Color.HSVToColor(Color.alpha(c),hsv);
}
Upvotes: 3