Android进阶知识(十九):属性动画及动画使用注意事项

本文深入讲解Android属性动画,包括ValueAnimator、ObjectAnimator和AnimatorSet的使用,插值器与估值器的自定义,以及动画监听器的应用。同时,探讨了任意属性动画化的技巧和注意事项,帮助开发者掌握高级动画技能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android进阶知识(十九):属性动画及动画使用注意事项

  View动画只能支持四种简单操作,除此之外View动画一个最大的缺陷:只是改变View的显示效果,并不会真正的改变View的属性。具体来说:例如屏幕左上角有一个Button,使用View动画将其移动到右下角,此刻点击右下角的Button,其绝对不会响应点击事件,而在左上角原位置则会响应。
在这里插入图片描述
  为此,在API11的时候加入了新特性——属性动画,它对作用对象进行了扩展,属性动画可以对任何对象做动画,甚至可以没有对象

一、属性动画使用

  属性动画中有ValueAnimator、ObjectAnimator和AnimatorSet等概念,通过它们可以实现绚丽的动画。属性动画可以对任意对象的属性进行动画,动画默认时间间隔300ms,默认帧率10ms/帧。
  值得一提的是,属性动画从API11才有,这严重限制了属性动画的使用,为了兼容以前的版本可以采用开源动画库nineoldandroids,但是其原理是内部通过代理View动画来实现的,因此在低版本上,其本质是View动画。
在这里插入图片描述
  属性动画常用的动画类:ValueAnimator、ObjectAnimator(继承至ValueAnimator)和AnimatorSet。

  1. ValueAnimator

  ValueAnimator的基础使用代码如下:

ValueAnimator valueAnim = ValueAnimator.ofFloat(0f, 1f);
valueAnim.setDuration(1000);
// 设置每一帧动画
valueAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator) {
        // 获取动画每一帧的值并设置每一帧动画
        mImageView.setAlpha((Float) valueAnimator.getAnimatedValue());
    }
});
valueAnim.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animator) {
        super.onAnimationEnd(animator);
        //TODO 当动画结束的时候做操作
    }
});
valueAnim.start();

  上述代码实现的功能是在1000ms内,View从透明到不透明的变换过程,其中AnimatorUpdateListener用于获取动画每一帧的返回值,设置每一帧的动画;AnimatorListenerAdapter用于指定动画结束事件发生时的处理方法(关于监听器可以见后续小节)。
在这里插入图片描述

  1. ObjectAnimator

  ObjectAnimator,继承ValueAnimator,允许指定target object,并且target object需要有setter方法,例如:ImageView的setRotation方法。

// 异步动画,执行起来会同时进行
ObjectAnimator.ofFloat(mImageView, "rotation", 0f, 360f)
    .setDuration(1000)
    .start();
ObjectAnimator.ofFloat(mImageView, "traslationX", 0f, 200f)
    .setDuration(1000)
    .start();
ObjectAnimator.ofFloat(mImageView, "traslationY", 0f, 200f)
    .setDuration(1000)
    .start();        

  上述三个属性动画会并行执行,原因在于属性动画是异步动画,即属性动画在调用start()方法之后是一个异步的过程,因此会同时执行三个动画。
  Google为异步动画提供了更好的实现异步动画的方法:PropertyValuesHolder。例子如下:

// 实现异步动画的另一种方法
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("rotation", 0F, 360F);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("traslationX", 0F, 200F);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("traslationY", 0F, 200F);
ObjectAnimator.ofPropertyValuesHolder(mImageView, holder1, holder2, holder3)
    .setDuration(1000)
    .start();

在这里插入图片描述

PropertyValuesHolder的优点:Google在其中对动画做了一些优化,这些优化使得在使用多个属性动画时,更加有效率,节省系统资源

  1. AnimatorSet

  AnimatorSet——动画集合,用于组合一系列动画,使用AnimatorSet可以实现动画的串行和并行执行

// 使用AnimatorSet可以实现动画的串行和并行执行
ObjcetAnimator animator1 = ObjectAnimator.ofFloat(mImageView, "rotation", 0f, 360f);
ObjcetAnimator animator2 = ObjectAnimator.ofFloat(mImageView, "traslationX", 0f, 200f);
ObjcetAnimator animator3 = ObjectAnimator.ofFloat(mImageView, "traslationY", 0f, 200f);
// 创建动画集合
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
// set.playTogether(animator1, animator2, animator3);  // 并行执行
// set.playSequentially(animator1, animator2, animator3);  // 串行执行
set.play(animator2).with(animator2);  // 并行执行
set.play(animator1).after(animator2);  // 在animator2之后执行,串行
set.start();

  属性动画不仅仅可以通过代码实现,也可以通过XML文件来实现,但是实际开发中建议使用代码进行开发。通过代码实现比较简单,并且属性动画的一个属性的起始值很多时候是无法提前确定的。

二、插值器与估值器

  定义属性动画的时候,可以通过自定义插值器和估值器实现更多的动画效果。

  1. 插值器——TimeInterpolator

  时间插值器定义了一个时间的映射关系,实现动画在执行时间内的加速、减速等,其作用是根据时间流逝的百分比来计算出当前属性值改变的百分比
  所有插值器都实现了TimeInterpolator接口,自定义插值器是可以通过实现TimeInterpolator接口。
  插值器的简单使用代码如下。

AnimatorSet set = new AnimatorSet();
set.playTogether(animator, animator1);
set.setDuration(500);
// 使用减速插值器
set.setInterpolator(new DecelerateInterpolator());
set.start();

  系统预置的插值器如下表所示。

插值器描述
LinearInterpolator线性插值
AccelerateInterpolator加速
DecelerateInterpolator减速
AccelerateDecelerateInterpolator先加速,后减速
AnticipateInterpolator先向后,再向前抛向终点
OvershootInterpolator向前抛出终点,再回到终点
AniticipateOvershootInterpolator先向后,再向前抛出终点,再回到终点
BounceInterpolator结束时反弹
CycleInterpolator循环播放
TimeInterpolator用于自定义

在这里插入图片描述

  1. 估值器——TypeEvaluator

  估值器的作用是根据当前属性改变的百分比来计算改变后的属性值,与属性的起始值、结束值、fraction三个值有关。
  默认情况下,默认的Evaluators将对起始值和结束值的间隔,依照时间进行等分,(在不设定Interpolator的情况下)从起始值均匀的增加(减低)到结束值。
  例如如下的动画中,默认的Evaluators将依照100ms对间隔1.0f进行等分,因此该动画每1ms增加的透明度为1/100f。

ObjectAnimator.ofFloat(mImageView, "alpha", 0f, 1f)
    .setDuration(100)
    .start();

  系统预置的插值器如下表所示。

估值器(Evaluator)描述
IntEvaluator用于评估int型的属性值
FloatEvaluator用于评估float型的属性值
ArgbEvaluator用于评估颜色的属性值,采用16进制
TypeEvaluator用于自定义估值器的接口

在这里插入图片描述

  所有的估值器(Evaluator)都实现了TypeEvaluator接口,通过实现该接口可以自定义估值器,接口如下。其中fraction为比例因子(0.0~1.0),用于计算转化后的值

public interface TypeEvaluator<T> {
    /**
    * @param fraction 比例因子
    * @param startValue 属性起始值
    * @param endValue 属性结束值
    * @return 转换后的值
    **/
    public T evaluate(float fraction, T startValue, T endValue);
}

  自定义Evaluator的例子如下:

// 设置起始值,终止值
PointOpsition startPoint = new PointOpsition(0, 0);
PointOpsition endPoint = new PointOpsition(radius, 60 * i + 360);
// 设置估值器,起始值和终止值
ValueAnimator animator = ValueAnimator.ofObject(new ExpandTypeEvaluator(), startPoint, endPoint);
animator.addUpdateListener((valueAnimator) -> {
    PointOpsition pointOpsition = (PointOpsition) valueAnimator.getAnimatedValue();
    button.setTranslationX(pointOpsition.getX());
    button.setTranslationY(pointOpsition.getY());
});
animator.start();

  实现了TypeEvaluator接口的估值器:

class ExpandTypeEvaluator implements TypeEvaluator<PointOpsition> {
    @Override
    public PointOpsition evaluate(float v, PointOpsition pointOpsition, PointOpsition t1) {
        float rRaduis = t1.getRadius() * v + (1 - v) * pointOpsition.getRadius();
        float rDegree = t1.getDegree() * v + (1 - v) * pointOpsition.getDegree();
        return new PointOpsition(rRaduis, rDegree);
    }
}

在这里插入图片描述

三、属性动画的监听器

  属性动画提供了监听器用于监听动画的播放过程,主要有如下两个接口:AnimatorUpdateListener和AnimatorListener。
  AnimatorListener的定义如下:
  从该接口定义可以看出,它可以监听动画的开始、结束、取消以及重复播放。同时为了方便开发,系统还提供了AnimatorListenerAdapter,它使得开发者可以选择性的实现上述四个方法
在这里插入图片描述
  AnimatorUpdateListener接口的定义如下。该接口会监听整个动画过程,动画由许多帧组成,每播放一帧,onAnimatorUpdate就会被调用一次

四、对任意属性做动画

  属性动画要求动画作用的对象提供该属性的get和set方法。因此对object的属性abc做动画,如果想要让动画生效,要同时满足两个条件:
  第一、object必须提供setAbc方法,如果动画的时候没有传递初始值,那么还要提供getAbc方法,因为系统要去取abc属性的初始值(如果这条不满足,程序直接Crash)。
  第二、object的setAbc属性对属性abc所做的改变必须能够通过某种方法反映出来,比如带来UI的改变(如果这条不满足,动画无效果但不会Crash)。
在这里插入图片描述
  对于第二种情况,官方文档提供了三种解决思路:

  • 给对象加上get和set方法,如果有权限的话。
  • 用一个类来包装原对象,间接为其提供get和set方法。
  • 采用ValueAnimator,监听动画过程,自定义实现属性的改变。

五、使用动画的注意事项

  通过动画可以实现一些比较绚丽的效果,但是在使用View动画、帧动画以及属性动画的过程中需要注意一些事项,如下表所示。

问题注意
OOM问题该问题出现在帧动画中,当图像数量过多且图片较大时,极易出现OOM
内存泄漏属性动画中的无限循环动画在Activity退出时及时停止,否则将导致Activity无法释放而造成内存泄漏。(View动画不存在该问题)
兼容性问题动画在3.0以下有兼容性问题,在某些特殊情景无法正常工作,需要做好适配
View动画问题View动画并不是真正改变View的状态,有时候动画完成后无法隐藏View(setVisibility(GONE)无效),这个时候只要调用view.clearAnimation()清除View动画即可解决
不要使用px尽量使用dp,使用px会导致不同设备上有不同的效果
动画元素的交互在Android 3.0以前的系统上,不管是View动画还是属性动画,在View移动后,原位置依旧可以触发单击事件。而3.0以后,属性动画的单击事件触发位置为移动后的位置,View动画仍然在原位置。
硬件加速使用动画过程中,建议开启硬件加速,以提高动画流程性

参考资料:《Android艺术开发探索》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值