如何在Android上动画化视图的背景颜色变化?

例如:

我有一个红色背景色的视图。视图的背景颜色变为蓝色。我怎样才能在颜色之间平滑过渡呢?

如果视图不能做到这一点,那么另一种选择是受欢迎的。


当前回答

这里有一个很好的函数可以做到这一点:

public static void animateBetweenColors(final @NonNull View viewToAnimateItsBackground, final int colorFrom,
                                        final int colorTo, final int durationInMs) {
    final ColorDrawable colorDrawable = new ColorDrawable(durationInMs > 0 ? colorFrom : colorTo);
    ViewCompat.setBackground(viewToAnimateItsBackground, colorDrawable);
    if (durationInMs > 0) {
        final ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
        colorAnimation.addUpdateListener(animator -> {
            colorDrawable.setColor((Integer) animator.getAnimatedValue());
            ViewCompat.setBackground(viewToAnimateItsBackground, colorDrawable);
        });
        colorAnimation.setDuration(durationInMs);
        colorAnimation.start();
    }
}

在Kotlin中:

@JvmStatic
fun animateBetweenColors(viewToAnimateItsBackground: View, colorFrom: Int, colorTo: Int, durationInMs: Int) {
    val colorDrawable = ColorDrawable(if (durationInMs > 0) colorFrom else colorTo)
    ViewCompat.setBackground(viewToAnimateItsBackground, colorDrawable)
    if (durationInMs > 0) {
        val colorAnimation = ValueAnimator.ofObject(ArgbEvaluator(), colorFrom, colorTo)
        colorAnimation.addUpdateListener { animator: ValueAnimator ->
            colorDrawable.color = (animator.animatedValue as Int)
            ViewCompat.setBackground(viewToAnimateItsBackground, colorDrawable)
        }
        colorAnimation.duration = durationInMs.toLong()
        colorAnimation.start()
    }
}

其他回答

我发现ArgbEvaluator在Android源代码中使用的实现在转换颜色方面做得最好。当使用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()方法)。当目标是API 22以下的代码时,将ArgbEvaluator源代码中找到的代码包装在你自己的方法中,如下所示:

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

你想怎么用就怎么用。

你可以使用API 11上面的ArgbEvaluatorCompat类。

implementation 'com.google.android.material:material:1.0.0' 


ValueAnimator colorAnim = ValueAnimator.ofObject(new ArgbEvaluatorCompat(), startColor, endColor);
colorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mTargetColor = (int) animation.getAnimatedValue();
        }
    });
colorAnim.start();

你可以像这样使用ValueAnimator:

 fun startColorAnimation(v: View) {
    val colorStart = v.solidColor
    val colorEnd = Color.RED
    val colorAnim: ValueAnimator = ObjectAnimator.ofInt(v, "backgroundColor", colorStart, colorEnd)
    colorAnim.setDuration(1000)
    colorAnim.setEvaluator(ArgbEvaluator())
    colorAnim.repeatCount = 1
    colorAnim.repeatMode = ValueAnimator.REVERSE
    colorAnim.start()
}

关于XML驱动动画的文档非常糟糕。我已经搜索了大约几个小时,只是为了在按下按钮时使背景颜色动画化…遗憾的是动画只有一个属性:你可以在选择器中使用exitFadeDuration:

<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:exitFadeDuration="200">

    <item android:state_pressed="true">
        <shape android:tint="#3F51B5" />
    </item>

    <item>
        <shape android:tint="#F44336" />
    </item>

</selector>

然后将它用作视图的背景。不需要Java/Kotlin代码。

这是我在Base Activity中用来改变背景的方法。我使用在代码中生成的GradientDrawables,但可以适应。

    protected void setPageBackground(View root, int type){
        if (root!=null) {
            Drawable currentBG = root.getBackground();
            //add your own logic here to determine the newBG 
            Drawable newBG = Utils.createGradientDrawable(type); 
            if (currentBG==null) {
                if(Build.VERSION.SDK_INT<Build.VERSION_CODES.JELLY_BEAN){
                    root.setBackgroundDrawable(newBG);
                }else{
                    root.setBackground(newBG);
                }
            }else{
                TransitionDrawable transitionDrawable = new TransitionDrawable(new Drawable[]{currentBG, newBG});
                transitionDrawable.setCrossFadeEnabled(true);
                if(Build.VERSION.SDK_INT<Build.VERSION_CODES.JELLY_BEAN){
                     root.setBackgroundDrawable(transitionDrawable);
                }else{
                    root.setBackground(transitionDrawable);
                }
                transitionDrawable.startTransition(400);
            }
        }
    }

更新:如果有人遇到同样的问题,我发现,出于某种原因,在Android <4.3使用setCrossFadeEnabled(true)导致一个不受欢迎的白色效果,所以我不得不切换到一个纯色<4.3使用@Roman Minenok ValueAnimator方法上面提到。