动画改变Android上的视图背景颜色

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

例如:

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

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

274342 次浏览

我最终想出了一个(非常好的)解决这个问题的方法!

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

请参阅罗马的回答以获得使用属性动画API的另一种解决方案,在这个答案最初发布时还不可用。

实现这一点的另一种简单方法是使用AlphaAnimation执行渐隐。

  1. 将视图设置为ViewGroup
  2. 在索引0处添加一个子视图,使用match_parent布局维度
  3. 给你的孩子与容器相同的背景
  4. 更改容器的背景为目标颜色
  5. 使用AlphaAnimation淡出子动画。
  6. 当动画完成时删除子动画(使用AnimationListener)

你可以为彩色动画使用new 属性动画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使用九个旧的机器人库从杰克沃顿。

getColor方法在Android M中已弃用,所以你有两个选择:

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

    ContextCompat.getColor(this, R.color.red);
    
  • if you don't use the support library, you need to replace the getColor calls with:

    getColor(R.color.red);
    

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

前两个使用Android属性动画框架。

在以下情况使用对象动画师:

  • 视图的背景颜色定义为xml文件中的argb值。
  • 你的视图之前已经通过view.setBackgroundColor()设置了它的颜色
  • 视图的背景颜色定义在一个可绘制对象中,定义了任何额外的属性,如描边或角半径。
  • 你的视图有它的背景颜色定义在一个可绘制的,你想要删除任何额外的属性,如笔画或角半径,请记住,删除额外的属性将不会动画。

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

在以下情况使用价值的动画师:

  • 你的视图有它的背景颜色定义在一个可绘制的,还设置属性,如笔画或角半径和你想改变它为一个新的颜色,这是在运行时决定的。

在以下情况使用可拉的过渡:

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

我有一些性能问题与过渡绘图运行,而我正在打开一个抽屉布局,我还没有能够解决,所以如果你遇到任何意外的口吃,你可能会遇到相同的bug,我有。

如果你想使用StateLists可拉的LayerLists可拉的,你必须修改Value Animator的例子,否则它会在final GradientDrawable background = (GradientDrawable) view.getBackground();行崩溃。

对象动画器:

视图定义:

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

你也可以像XMight在Android objectAnimator动画布局的backgroundColor中那样,使用AnimatorInflater从xml中加载动画定义

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

过渡可绘制:

视图定义:

<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。

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

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

这是我在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)会导致不受欢迎的白色效果,所以我不得不使用上面提到的@Roman Minenok ValueAnimator方法为<4.3切换到纯色。

你可以创建一个对象动画器。例如,我有一个targetView,我想改变你的背景颜色:

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

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

Enter image description here

这段代码将帮助你:

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

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

如果你只是想做动画,可以使用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源代码中找到的代码包装在你自己的方法中,如下所示:

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

你想怎么用就怎么用。

最好的方法是使用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();

答案可以从很多方面给出。你也可以使用ValueAnimatorofArgb(startColor,endColor)

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

res文件夹中添加一个文件夹动画师。(名称必须是动画师)。添加一个动画资源文件。例如< em > res /动画/ fade.xml < / em >

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

关于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代码。

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

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

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

传递任何你想改变颜色的视图。

你可以在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();

罗马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)

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