54

假设我有两种颜色,我需要创建一个可以快速从一种颜色切换到另一种颜色的实时动画。

我试图只是增加颜色十六进制直到我到达另一个,但这给出了一个非常糟糕的动画,因为它显示了许多不相关的颜色。

我正在使用setColorFilter(color, colorfilter)更改图像视图的颜色。

改变 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);
    }
4

12 回答 12

77

结合@zed 和@Phil 的答案,使用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();                                        

HSV 将提供比 Android 的默认颜色空间更好的过渡,因为 HSV 以柱坐标描述颜色,可以很好地分离颜色的属性并允许在单个轴上平滑过渡。您可以从下图中看到,沿 H、S 或 V 方向行进会在颜色之间提供很好的连续过渡。

在此处输入图像描述

于 2014-07-08T21:34:43.713 回答
64

您可以简单地使用ArgbEvaluator自 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();

更好的是,从 API 21 (Lollipop 5.0) 开始,您可以将上面代码中的前 3 行替换为:

ValueAnimator anim = ValueAnimator.ofArgb(color1, color2)
于 2015-03-22T11:17:42.853 回答
20

您可以使用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();

您还可以在调用之前设置持续时间或其他参数anim.start()。例如:

anim.setDuration(400);

将动画持续时间设置为 400 毫秒。


最后,请注意,ValueAnimator它从Honeycomb开始可用,因此如果您支持较旧的 SDK,则可以使用NineOldAndroids

于 2013-08-13T18:59:02.723 回答
12

在 ColorUtils 中使用 blendARGB 的最佳方式。

论文和最小行代码。

view.setBackgroundColor(ColorUtils.blendARGB(Color.parseColor("#FFFFFF"), Color.parseColor("#CCCCCC"), fraction));
于 2017-08-27T09:47:25.153 回答
11

当转换非常不同的颜色时,其他答案给了我一种奇怪的效果。从黄色到灰色,它会在动画期间的某个时刻变为绿色。

最适合我的是以下代码段。这创造了一个非常平滑的过渡,中间没有出现奇怪的颜色。

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);
}
于 2015-01-30T15:59:53.467 回答
8

您可以使用 TransitionDrawable

TransitionDrawable transition = (TransitionDrawable) viewObj.getBackground();
transition.startTransition(transitionTime);

在可绘制文件夹中创建 xml 文件,您可以编写如下内容:

<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/end_color" />
    <item android:drawable="@drawable/start_color" />
</transition>

然后,在实际 View 的 xml 中,您将在 android:background 属性中引用此 TransitionDrawable。

于 2014-09-04T11:42:09.737 回答
4

以下片段对我来说非常有效。我想过使用 ValueAnimator.ofArgb() 但这需要至少 api 21。相反,以下内容适用于 api 11 及更高版本。它实现了颜色的平滑变化。

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();
于 2015-10-29T01:29:47.583 回答
4

我发现ArgbEvaluatorandroid 源代码中使用的实现在转换颜色方面做得最好。使用 HSV 时,根据这两种颜色,过渡对我来说跳过了太多的色调。但是这种方法没有。如果您尝试按照此处的建议简单ArgbEvaluator使用ValueAnimator动画:

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

但是,如果您像我一样想要将您的转换与某些用户手势或从输入传递的其他值联系起来,那么这ValueAnimator并没有多大帮助(除非您的目标是 API 22 及更高版本,在这种情况下您可以使用该ValueAnimator.setCurrentFraction()方法)。ArgbEvaluator当目标低于 API 22 时,将源代码中的代码包装在您自己的方法中,如下所示:

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

并随心所欲地使用它。

于 2017-01-16T17:56:12.070 回答
4

如果您像我一样尝试从一种 RGB 颜色更改为白色或黑色,您会发现使用 @bcorso 的答案很困难,因为它对于该特定工作来说是错误的颜色空间。并不是说答案是错误的,这不是我要说的。

这是一个使用颜色进行混合的示例colorA,使用白色(可以用黑色代替):

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

两者似乎有很多混淆,所以这里有一张视觉描述它的图片。 HSL 和 HSV

于 2018-03-15T11:08:27.723 回答
3

解决方案:我通过递归变换色调来解决它

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);
    }
于 2014-05-07T19:20:38.867 回答
1

我尝试了@bcorso 和@damienix,但他们的解决方案对我不起作用。该函数的数学运算要简单得多,因为它遵循基本的插值公式:

p(t) = (1 - t) * p1 + t * p2,其中 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);
}
于 2016-06-19T02:49:06.030 回答
0

首先,我要感谢 @bcorso 提供有关 HSV 的出色答案和数据。

当我使用所提出的算法有一段时间时,我有点恼火,因为它会在这个过程中使颜色从一种颜色转变为另一种触摸无关的颜色。所以,我终于实现了我自己的愚蠢的简单算法,它使用真实的 3D 坐标来进行这样的插值:

https://github.com/konmik/animated-color

于 2015-10-19T21:07:22.990 回答