android: Animate color change from color to color

AndroidColors

Android Problem Overview


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);
	}

Android Solutions


Solution 1 - Android

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.

enter image description here

Solution 2 - Android

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)

Solution 3 - Android

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.

Solution 4 - Android

best way use blendARGB in ColorUtils.

esay and minimum line code.

view.setBackgroundColor(ColorUtils.blendARGB(Color.parseColor("#FFFFFF"), Color.parseColor("#CCCCCC"), fraction));

Solution 5 - Android

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);
}

Solution 6 - Android

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.

Solution 7 - Android

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();

Solution 8 - Android

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.

Solution 9 - Android

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. HSL and HSV

Solution 10 - Android

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);
    }

Solution 11 - Android

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);
}

Solution 12 - Android

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionzedView Question on Stackoverflow
Solution 1 - AndroidbcorsoView Answer on Stackoverflow
Solution 2 - AndroiddimsuzView Answer on Stackoverflow
Solution 3 - AndroidPhilView Answer on Stackoverflow
Solution 4 - AndroidRasoul MiriView Answer on Stackoverflow
Solution 5 - AndroidfehbariView Answer on Stackoverflow
Solution 6 - AndroidMuhammad Aamir AliView Answer on Stackoverflow
Solution 7 - AndroidpatelbView Answer on Stackoverflow
Solution 8 - AndroidfahmyView Answer on Stackoverflow
Solution 9 - AndroidRasiveView Answer on Stackoverflow
Solution 10 - AndroidzedView Answer on Stackoverflow
Solution 11 - AndroidKarim FikaniView Answer on Stackoverflow
Solution 12 - AndroidkonmikView Answer on Stackoverflow