【问题标题】:Animate change of view background color on AndroidAndroid上视图背景颜色的动画变化
【发布时间】:2011-02-06 13:23:07
【问题描述】:

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

例如:

我有一个红色背景色的视图。视图的背景颜色变为蓝色。如何在颜色之间进行平滑过渡?

如果视图无法做到这一点,欢迎使用替代方法。

【问题讨论】:

  • 人们会期望颜色之间的平滑过渡对于像 Android 这样的平台来说应该不是问题。不过,也许视图不是为此而构建的。问题仍然存在。

标签: android animation view background-color


【解决方案1】:

您可以使用新的Property Animation Api 进行彩色动画:

int colorFrom = getResources().getColor(R.color.red);
int colorTo = getResources().getColor(R.color.blue);
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.setDuration(250); // milliseconds
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {

    @Override
    public void onAnimationUpdate(ValueAnimator animator) {
        textView.setBackgroundColor((int) animator.getAnimatedValue());
    }

});
colorAnimation.start();

为了向后兼容 Android 2.x,请使用来自 Jake Wharton 的 Nine Old Androids library

getColor 方法在 Android M 中已弃用,因此您有两种选择:

  • 如果您使用支持库,则需要将 getColor 调用替换为:

    ContextCompat.getColor(this, R.color.red);
    
  • 如果您不使用支持库,则需要将 getColor 调用替换为:

    getColor(R.color.red);
    

【讨论】:

  • "musthave" 解决方案,如果您使用的是 Property Animation API(或 NineOldAndroid)。动画非常流畅。
  • 有没有办法这样设置过渡的持续时间?
  • @iGio90 是的,你不会相信,但是方法调用 setDuration() :)
  • ValueAnimator.ofArgb(colorFrom, colorTo) 看起来更重要,但遗憾的是它是新的。
  • 漂亮的解决方案!谢谢。
【解决方案2】:

我最终为这个问题找到了一个(相当不错的)解决方案!

您可以使用TransitionDrawable 来完成此操作。例如,在可绘制文件夹中的 XML 文件中,您可以编写如下内容:

<?xml version="1.0" encoding="UTF-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- The drawables used here can be solid colors, gradients, shapes, images, etc. -->
    <item android:drawable="@drawable/original_state" />
    <item android:drawable="@drawable/new_state" />
</transition>

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

此时,您可以通过以下方式在代码中按命令启动转换:

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

或者通过调用反向运行过渡:

transition.reverseTransition(transitionTime);

请参阅 Roman's answer 了解使用 Property Animation API 的另一个解决方案,该解决方案在最初发布此答案时不可用。

【讨论】:

  • 感谢 mxrider!这确实回答了这个问题。不幸的是,它对我不起作用,因为将 TransitionDrawable 作为背景的视图也必须进行动画处理,并且视图动画似乎覆盖了背景转换。有什么想法吗?
  • 通过在启动动画后启动TransitionDrawable来解决它。
  • 太棒了!真高兴你做到了!你可以在 Android 的 xml 中做很多很酷的事情——其中大部分都隐藏在文档中,在我看来并不明显。
  • 一点也不明显。我应该提一下,在我的情况下我没有使用 xml,因为颜色是动态的。如果您通过代码创建 TransitionDrawable,您的解决方案也有效。
  • 主要缺点之一是没有动画完整的监听器
【解决方案3】:

根据您的视图如何获得其背景颜色以及您如何获得目标颜色,有几种不同的方法可以做到这一点。

前两个使用Android Property Animation 框架。

在以下情况下使用Object Animator

  • 您的视图将其背景颜色定义为 xml 文件中的 argb 值。
  • 您的视图之前的颜色由view.setBackgroundColor() 设置
  • 您的视图在可绘制对象中定义了其背景颜色,定义了任何额外的属性,例如描边或角半径。
  • 您的视图在可绘制对象中定义了其背景颜色,并且您想要删除任何额外的属性,例如笔划或角半径,请记住,额外属性的删除不会动画。

对象动画师通过调用view.setBackgroundColor 来工作,它会替换定义的可绘制对象,除非它是ColorDrawable 的实例,它很少是。这意味着来自可绘制对象的任何额外背景属性(如笔划或角)都将被删除。

在以下情况下使用Value Animator

  • 您的视图在可绘制对象中定义了其背景颜色,该可绘制对象还设置了笔划或角半径等属性,并且您希望将其更改为运行时确定的新颜色。

在以下情况下使用Transition drawable

  • 您的视图应在部署前已定义的两个可绘制对象之间切换。

我在打开一个我无法解决的 DrawerLayout 时运行的 Transition drawables 遇到了一些性能问题,所以如果您遇到任何意外的卡顿,您可能会遇到与我相同的错误。

如果您想使用StateLists drawableLayerLists drawable,则必须修改Value Animator 示例,否则它将在final GradientDrawable background = (GradientDrawable) view.getBackground(); 行上崩溃。

Object Animator:

查看定义:

<View
    android:background="#FFFF0000"
    android:layout_width="50dp"
    android:layout_height="50dp"/>

像这样创建和使用ObjectAnimator

final ObjectAnimator backgroundColorAnimator = ObjectAnimator.ofObject(view,
                                                                       "backgroundColor",
                                                                       new ArgbEvaluator(),
                                                                       0xFFFFFFFF,
                                                                       0xff78c5f9);
backgroundColorAnimator.setDuration(300);
backgroundColorAnimator.start();

您还可以使用 AnimatorInflater 从 xml 加载动画定义,就像 XMight 在 Android objectAnimator animate backgroundColor of Layout 中所做的那样

Value Animator:

查看定义:

<View
    android:background="@drawable/example"
    android:layout_width="50dp"
    android:layout_height="50dp"/>

可绘制定义:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#FFFFFF"/>
    <stroke
        android:color="#edf0f6"
        android:width="1dp"/>
    <corners android:radius="3dp"/>

</shape>

像这样创建和使用 ValueAnimator:

final ValueAnimator valueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(),
                                                           0xFFFFFFFF,
                                                           0xff78c5f9);

final GradientDrawable background = (GradientDrawable) view.getBackground();
currentAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

    @Override
    public void onAnimationUpdate(final ValueAnimator animator) {
        background.setColor((Integer) animator.getAnimatedValue());
    }

});
currentAnimation.setDuration(300);
currentAnimation.start();

Transition drawable:

查看定义:

<View
    android:background="@drawable/example"
    android:layout_width="50dp"
    android:layout_height="50dp"/>

可绘制定义:

<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <solid android:color="#FFFFFF"/>
            <stroke
                android:color="#edf0f6"
                android:width="1dp"/>
            <corners android:radius="3dp"/>
        </shape>
    </item>

    <item>
        <shape>
            <solid android:color="#78c5f9"/>
            <stroke
                android:color="#68aff4"
                android:width="1dp"/>
            <corners android:radius="3dp"/>
        </shape>
    </item>
</transition>

像这样使用 TransitionDrawable:

final TransitionDrawable background = (TransitionDrawable) view.getBackground();
background.startTransition(300);

您可以通过在动画实例上调用.reverse() 来反转动画。

还有其他一些制作动画的方法,但这三种可能是最常见的。我通常使用 ValueAnimator。

【讨论】:

  • 实际上可以在代码中定义和使用TransitionDrawable,但是由于某种原因,第二次就不行了。很奇怪。
  • 即使我的视图设置了 ColorDrawable,我也可以使用对象动画器。我只是将它保存到一个局部变量中,将其设置为 null,当动画完成后,将原来的 ColorDrawable 设置回来。
【解决方案4】:

您可以制作对象动画师。例如,我有一个 targetView,我想改变你的背景颜色:

int colorFrom = Color.RED;
int colorTo = Color.GREEN;
int duration = 1000;
ObjectAnimator.ofObject(targetView, "backgroundColor", new ArgbEvaluator(), colorFrom, colorTo)
    .setDuration(duration)
    .start();

【讨论】:

  • 从 API 21 开始,您可以使用 ofArgb(targetView, "backgroundColor", colorFrom, colorTo)。它的实现就是ofInt(...).setEvaluator(new ArgbEvaluator())
【解决方案5】:

如果你想要这样的彩色动画,

此代码将帮助您:

ValueAnimator anim = ValueAnimator.ofFloat(0, 1);   
anim.setDuration(2000);

float[] hsv;
int runColor;
int hue = 0;
hsv = new float[3]; // Transition color
hsv[1] = 1;
hsv[2] = 1;
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {

        hsv[0] = 360 * animation.getAnimatedFraction();

        runColor = Color.HSVToColor(hsv);
        yourView.setBackgroundColor(runColor);
    }
});

anim.setRepeatCount(Animation.INFINITE);

anim.start();

【讨论】:

  • anim变量初始化的部分在哪里?
  • @VoW ,它只是 ValueAnimator 的一个实例。
  • 应该是 anim.setRepeatCount(ValueAnimator.INFINITE); 而不是 Animation.INFINITE 现在它是一样的,但我们不能保证
  • @MikhailKrishtop Animation.INFINITE 有什么问题? developer.android.com/reference/android/view/animation/…
【解决方案6】:

最好的方法是使用 ValueAnimatorColorUtils.blendARGB

 ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
 valueAnimator.setDuration(325);
 valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {

              float fractionAnim = (float) valueAnimator.getAnimatedValue();

              view.setBackgroundColor(ColorUtils.blendARGB(Color.parseColor("#FFFFFF")
                                    , Color.parseColor("#000000")
                                    , fractionAnim));
        }
});
valueAnimator.start();

【讨论】:

    【解决方案7】:

    XML 驱动动画的文档很糟糕。我已经搜索了大约几个小时,只是为了在按下按钮时为按钮的背景颜色设置动画......可悲的是,动画只有一个属性:您可以在selector 中使用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>
    

    然后将其用作background 以供您查看。无需 Java/Kotlin 代码。

    【讨论】:

    • 优秀的答案。请注意,您可能还需要android:enterFadeDuration;我的经验是,没有它,我的动画第二次无法正常工作。
    • @String,奇怪,在模拟器上它可以在没有android:enterFadeDuration 的情况下工作很多次。
    【解决方案8】:

    实现此目的的另一种简单方法是使用 AlphaAnimation 执行淡入淡出。

    1. 将您的视图设为 ViewGroup
    2. 在索引 0 处添加一个子视图,其布局尺寸为 match_parent
    3. 为您的孩子提供与容器相同的背景
    4. 将容器的背景更改为目标颜色
    5. 使用 AlphaAnimation 淡出孩子。
    6. 动画完成后移除子项(使用 AnimationListener)

    【讨论】:

      【解决方案9】:

      这是我在 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 setCrossFadeEnabled(true) 会导致不希望出现的白色效果,因此我不得不切换到纯色

      【讨论】:

      • 这正是我想要的。谢谢! :)
      【解决方案10】:

      答案以多种方式给出。您也可以使用ofArgb(startColor,endColor)ValueAnimator

      对于 API > 21:

      int cyanColorBg = ContextCompat.getColor(this,R.color.cyan_bg);
      int purpleColorBg = ContextCompat.getColor(this,R.color.purple_bg);
      
      ValueAnimator valueAnimator = ValueAnimator.ofArgb(cyanColorBg,purpleColorBg);
              valueAnimator.setDuration(500);
              valueAnimator.setInterpolator(new LinearInterpolator());
              valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                         relativeLayout.setBackgroundColor((Integer)valueAnimator.getAnimatedValue());
                    }
              });
              valueAnimator.start();
      

      【讨论】:

        【解决方案11】:

        这是一个很好的函数:

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

        【讨论】:

        • 这个不支持低端手机
        • @Kartheeks 你的意思是 Android 10 及更低版本?如果是这样,您可以轻松地使用“nineOldAndroids”库来支持它:nineoldandroids.com。语法几乎完全相同。
        • 对于低版本 colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { colorDrawable.setColor((Integer) animation.getAnimatedValue()); ViewCompat.setBackground( viewToAnimateItsBackground, colorDrawable); } });
        【解决方案12】:

        对 Kotlin 使用以下函数:

        private fun animateColorValue(view: View) {
            val colorAnimation =
                ValueAnimator.ofObject(ArgbEvaluator(), Color.GRAY, Color.CYAN)
            colorAnimation.duration = 500L
            colorAnimation.addUpdateListener { animator -> view.setBackgroundColor(animator.animatedValue as Int) }
            colorAnimation.start()
        }
        

        传递您想要更改颜色的任何视图。

        【讨论】:

          【解决方案13】:

          将文件夹 animator 添加到 res 文件夹中。 (名称必须是animator)。添加一个动画资源文件。例如 res/animator/fade.xml

          <?xml version="1.0" encoding="utf-8"?>
          <set xmlns:android="http://schemas.android.com/apk/res/android">
              <objectAnimator
                  android:propertyName="backgroundColor"
                  android:duration="1000"
                  android:valueFrom="#000000"
                  android:valueTo="#FFFFFF"
                  android:startOffset="0"
                  android:repeatCount="-1"
                  android:repeatMode="reverse" />
          </set>
          

          在Activity java文件中,调用这个

          View v = getWindow().getDecorView().findViewById(android.R.id.content);
          AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.fade);
          set.setTarget(v);
          set.start();
          

          【讨论】:

            【解决方案14】:

            我发现 Android 源代码中 ArgbEvaluator 使用的实现在转换颜色方面做得最好。使用 HSV 时,根据这两种颜色,过渡对我来说跳过了太多的色调。但是这个方法不行。

            如果您想简单地制作动画,请按照建议的here 使用ArgbEvaluatorValueAnimator

            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 source code 中的代码包装在您自己的方法中,如下所示:

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

            随心所欲地使用它。

            【讨论】:

              【解决方案15】:

              你可以像这样使用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()
              }
              

              【讨论】:

                【解决方案16】:

                基于ademar111190's answer,我创建了这个方法,它将在任意两种颜色之间脉冲视图的背景颜色:

                private void animateBackground(View view, int colorFrom, int colorTo, int duration) {
                
                
                    ObjectAnimator objectAnimator = ObjectAnimator.ofObject(view, "backgroundColor", new ArgbEvaluator(), colorFrom, colorTo);
                    objectAnimator.setDuration(duration);
                    //objectAnimator.setRepeatCount(Animation.INFINITE);
                    objectAnimator.addListener(new Animator.AnimatorListener() {
                        @Override
                        public void onAnimationStart(Animator animation) {
                
                        }
                
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            // Call this method again, but with the two colors switched around.
                            animateBackground(view, colorTo, colorFrom, duration);
                        }
                
                        @Override
                        public void onAnimationCancel(Animator animation) {
                
                        }
                
                        @Override
                        public void onAnimationRepeat(Animator animation) {
                
                        }
                    });
                    objectAnimator.start();
                }
                

                【讨论】:

                  【解决方案17】:

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

                  【讨论】:

                    【解决方案18】:

                    Roman Minenok 在 kotlin 中回答并作为扩展函数

                    fun View.colorTransition(@ColorRes startColor: Int, @ColorRes endColor: Int, duration: Long = 250L){
                        val colorFrom = ContextCompat.getColor(context, startColor)
                        val colorTo =  ContextCompat.getColor(context, endColor)
                        val colorAnimation: ValueAnimator = ValueAnimator.ofObject(ArgbEvaluator(), colorFrom, colorTo)
                        colorAnimation.duration = duration
                    
                        colorAnimation.addUpdateListener {
                            if (it.animatedValue is Int) {
                                val color=it.animatedValue as Int
                                setBackgroundColor(color)
                            }
                        }
                        colorAnimation.start()
                    }
                    

                    如果你想从当前的背景颜色更改为新的颜色,那么你可以使用这个

                    fun View.colorTransition(@ColorRes endColor: Int, duration: Long = 250L){
                        var colorFrom = Color.TRANSPARENT
                        if (background is ColorDrawable)
                            colorFrom = (background as ColorDrawable).color
                    
                        val colorTo =  ContextCompat.getcolor(context, endColor)
                        val colorAnimation: ValueAnimator = ValueAnimator.ofObject(ArgbEvaluator(), colorFrom, colorTo)
                        colorAnimation.duration = duration
                    
                        colorAnimation.addUpdateListener {
                            if (it.animatedValue is Int) {
                                val color=it.animatedValue as Int
                                setBackgroundColor(color)
                            }
                        }
                        colorAnimation.start()
                    }
                    

                    用法

                    myView.colorTransition(R.color.bg_color)
                    

                    【讨论】:

                      猜你喜欢
                      • 2015-08-11
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2013-07-28
                      相关资源
                      最近更新 更多