Android动画-Property Animation

Android动画-Property Animation

  • Android动画-Property Animation
    • 前言
    • Property Animation 常见特性
    • Property Animation是如何工作的
    • Property Animation 和 View Animation的不同点
    • ValueAnimator
    • ObjectAnimator
    • AnimatorSet
    • 动画监听状态
    • 自定义TypeEvaluator
      • 源码FloatEvaluator
      • 自定义TypeEvaluator流程
    • 使用Interpolators

Android动画-Property Animation

前言

Property Animation是通过改变对象的属性值来创建动画的机制。尽管View Animation提供了Tween Animation 和 Frame Animation这些动画能解决我们开发过程中常用的大多数动画特效,然后View Animation还是有很多的限制,比如Tween Animation 仅仅支持平移、缩放等这些操作,Frame Animation 是直接对多张图的逐帧播放,这些都收到很大的限制,然而在Android 3.0之后, google 提出了 Property Animation,顾名思义,我们可以使用Property Animation 对对象的属性创建动画,比如View 对象的背景颜色、水平方向垂直方向的位置、以及View提供的其他属性,可以说Property Animation 几乎无所不能,借下来这篇文章我将带领大家了解Property Animation 到底是如何实现的。

Property Animation 常见特性

  • Duration:你能指定动画的持续时间,默认为300ms.
  • Time interpolation: 指定插值器,TimeInterpolator是所有插值器的接口,用来定义变化率。可以指定如何将属性值作为动画当前运行时间的函数进行计算。
  • Repeat count and behavior:可以定义重复次数以及重复的模式。
  • Animator sets:动画集
  • Frame refresh delay:可以指定刷新动画的帧的频率,默认为10ms刷新一次。但是实际上刷新的时间视系统当前时间是否繁忙为准。

Property Animation是如何工作的

下面让我们先来看一下Property是如何进行运动的:
图片1-匀速运动
上图一展示的是一个我们假设的对象在水平方向上移动了40个px,花费了40ms.以10ms刷新一次动画,四次10ms都运动了10px,采用的是线性匀速运动,以相同的速率进行移动。
img
上图二展示了非线形运动的图示。以水平方向开始和结束位置相同,同样时间内采用不用的速率进行移动。从图中我们可以看出,以小x=20中间点为例,该动画从起点加速到中间点,然后从中间点再以减速运动移动。

下面我们再来讨论下 property Animation 系统是如何通过列出的重要组件进行计算的
[外链图片转存失败(img-JooCXZ48-1563449081754)(https://developer.android.com/images/animation/valueanimator.png)]
上图三列出了一些property Animation的重要组件:

  • ValueAnimator:用于跟踪动画计时,比如现在动画跑了多久、当前的属性值。
  • TimeInterpolator:用来定义插值器,用来计算插值分数,其子类为AccelerateInterpolator、AccelerateDecelerateInterpolator等。
  • TypeEvaluator:用来定义如何计算当前动画值,evaluate()传入当前插值分数,开始值和结束值。
  • ValueAnimator.Animator.UpdateListener:监听属性的改变

比如,在图二中使用的TimeInterpolator是AccelerateDecelerateInterpolator,使用的TypeEvaluator是系统定义好的IntEvaluator。

当我们创建一个Property Animation时,创建一个ValueAnimatior对象,提供一个开始和结束值、动画持续时间。当调用ValueAnimatior.start()动画开始,在整个动画过程中ValueAnimator计算过去时分数,过去时分数介于0-1之间、是通过经过的时间/动画持续的时间来计算的。分数因子类比百分比数,0等于0%,1等价于100%。比如图一在10ms的时候分数因子为10/40=.25.

ValueAnimator计算出过去时分数后,它会调用当前设置的TimeInterpolator来计算插值分数插值分数是通过输入过去时分数这个变量,通过当前设置的TimeInterpolator公式进行计算的。比如图二中,因为物体会缓慢加速,所以在10ms内插值分数为.15少于.25,而图一是以一种匀速运动运动一直运动,插值分数不会改变。

插值分数计算出来后,ValueAnimator会调用合适的TypeEvaluator进行计算当前动画的属性值,计算变量为插值分数、开始值、结束值。比如图二中,在10ms的时候插值分数为.15,所以当前动画的属性值为.15*(40-0).

完成整个计算动画的流程后,我们进行整理:

  • ValueAnimator计算出过去时/总时间的过去时分数
  • ValueAnimator根据当前设置的TimeInterpolator计算出插值分数
  • ValueAnimator使用合适的ValueAnimator根据插值分数、开始值、结束值计算当前的动画的属性值

Property Animation 和 View Animation的不同点

  • View Animation 仅仅支持开放的几个API,比如对View Object 缩放、平移之类,而不能以背景色为改变动画
  • View Animation仅能对View Object设置动画 ,Property Animation可以为非View Object设置动画
  • View Animation仅修改绘制View的位置,而不是实际View本身,比如点击的时候还是仅能点击移动前的位置才有响应,而Property Animation 修改的直接是View 的实际位置,就没有这些问题
  • 因为View Animation 代码更简单、开发时效更快。所以在我们实际开发中,如果View Animation如果已经能满足我们的要求,那么我们就不必要使用Property Animation.

ValueAnimator

PropertyAnimation创建动画是通过两个步骤来走的:

  • 第一步是计算每个动画时间更改的值
  • 第二步是给目标对象设置改变属性值

Property Animation是如何工作的一章我们了解到,ValueAnimator完成了第一步,而第二步需要我们编写逻辑来实现,使用ValueAnimator的流程如下:

  1. 使用ValueAnimator.ofInt()、ValueAnimator.ofArgb()、ValueAnimator.ofFloat()工厂方法创建ValueAnimtor对象
  2. 设置常用配置信息,比如动画持续时间(duration)、重复次数(repeatCount)、重复模式(repeat)、插值器(interpolator)、动画值更新监听器(updateListener)等
  3. 在动画值更新监听器获得当前动画值,修改属性

下面我们列出代码示例:

    ValueAnimator.ofFloat(0f, 100f).apply {
      	//设置动画持续时间
        duration = 4000
      	//设置重复次数
        repeatCount=10
      	//重复模式
        repeatMode=ValueAnimator.REVERSE
      	// 设置加速插值器
        interpolator=AccelerateInterpolator()
        addUpdateListener { updateAnimation ->
            //改变目标的属性值
            iv_image.translationX = (updateAnimation.animatedValue as Float)
        }
        start()
    }

代码中,展示了一个简单以加速运动在水平方向从0平移100的动画特效,持续4000模式,重复10次。

ObjectAnimator

ObjectAnimatorValueAnimation的子类,它对ValueAnimation进一步封装,主要封装了对目标对象设置改变的属性值,以使得创建属性动画的代码更加简洁、开发速度更快速。

还是之前的平移动画效果,我们看看使用ObjectAnmator是如何实现上面的代码的:

    ObjectAnimator.ofFloat(iv_image, "translationX", 0f, 100f).apply {
        duration = 4000
        repeatCount=10
        //设置重复次数
        repeatMode=ValueAnimator.REVERSE
        //设置加速插值器
        interpolator=AccelerateInterpolator()
        start()
    }

代码和之前的有点类似,也是使用工厂方法创建ObjectAnimator传入目标对象,想要修改的属性。ObjectAnimator注重于帮助我们设置改变的属性,不再需要编写addUpdateListener中的代码,极大的增加了代码的简洁性。

在使用ObjectAnimator我们必须确保一些前置条件,否则属性动画将无法设置属性:

  • 确保目标动画有**set()**格式的方法
    • 给目标对像添加setXXX方法
    • 假如原始目标对象无法修改,那么我们创建一个包裹类,创建**set()**转发到真正需要修改的目标对象
    • 如果以上两种条件都不允许,那么我们只能创建ValueAnimator
  • 如果仅仅在工厂方法ofFloat(…)中传入一个值,默认该值为结束值,因为我们还需要取到一个初始值,所以需要确保有一个get 的方法。比如Foo属性值,需要一个getFoo()的方法
  • 设置动画属性的set和get方法的类型,必须与指定ObjectAnimator的起始值和结束值一致。比如ObjectAnimator.ofFloat(targetObject, “propName”, 1f)限定了set和get属性方法的类型为Float.
  • 根据一些属性和对象,有时候需要显式的在调用View对象的invalidate()。类似View对象中setTranslationX()、setScaleX()、setScaleY()这些方法内部调用了invalidate()所以不必显式调用。

AnimatorSet

有时候我们需要对动画进行组合,AnimatorSet就能把几个单个动画特效整合成一个组合动画,并且可以控制单个动画的执行顺序。

AnimatorSetAnimator的子类,控制者动画集合,根据Animator提供的API控制顺序:

  • play():创建依赖项的动画,以此动画为原点。比如play(a1) ,如果之后带上before和after以a1作为坐标系
  • with():同时执行,比如play(a1).with(a2) a1和a2同时执行
  • before():在…之前,比如play(a1).before(a2) a1在a2之前执行
  • after():在…之后,比如play(a1).after(a2) a1在a2之后执行

代码示例:

val translateAnimation = ObjectAnimator.ofFloat(iv_image, "translationX", 0f, 100f).apply {
                duration = 1000
            }
            val translateAnimation1 = ObjectAnimator.ofFloat(iv_image, "translationX", 0f, 200f).apply {
                duration = 1000
            }
            val translateAnimation2 = ObjectAnimator.ofFloat(iv_image, "translationX", 0f, 300f).apply {
                duration = 1000
            }
            val scaleXAnimator = ObjectAnimator.ofFloat(iv_image, "scaleX", 0f, 1f).apply {
                duration = 1000
            }
            // 创建AnimationSet
            val boundAnimbouncer = AnimatorSet().apply {
                // translateAnimation在scaleXAnimator之前执行
                play(translateAnimation).before(scaleXAnimator)
                // translateAnimation在translateAnimation1之后执行
                play(translateAnimation).after(translateAnimation1)
                // translateAnimation和translateAnimation1一起执行
                play(translateAnimation).with(translateAnimation2)
            }
            // 创建透明度改变动画
            val faceAnim = ObjectAnimator.ofFloat(iv_image, "alpha", 0f, 1f).apply {
                duration = 5000
            }

            AnimatorSet().apply {
                //faceAnim在boundAnimbouncer之前执行
                play(faceAnim).before(boundAnimbouncer)
                start()
            }

在使用AnimatorSet之前,我们需要创建单独的动画效果:

translateAnimation平移100、translateAnimation1平移200、translateAnimation2平移300、scaleXAnimator水平方向缩放,faceAnim透明度改变动画。

在我们设置的执行顺序时,真实的动画效果顺序应当以如下顺序执行:

  1. faceAnim执行
  2. translateAnimation1执行
  3. translateAnimation、translateAnimation2一起执行
  4. scaleXAnimator执行

动画监听状态

你可以使用**Animator.AnimatorListener()**监听在动画过程中一些重要的事件:

  • onAnimationStart():当动画开始的时候回调
  • onAnimationEnd():当动画结束的时候回调
  • onAnimationRepeat():当动画重复的时候回调
  • onAnimationCancel():当动画被取消的时候回调,不管是否结束动画,相应的也会回调onAnimationEnd()方法

你可以使用**ValueAnimator.AnimatorUpdateListener()**监听动画的每一帧。在动画过程中是使用ValueAnimator对象来计算的。为了使用计算值,你可以通过ValueAnimator.getAnimatedValue()获得当前动画计算的值。更多的详情可以查看-ValueAnimator一章。

如果你不想实现所有的Animator.AnimatorListener()接口方法,你也可以继承AnimatorListenerAdapter替代**Animator.AnimatorListener()接口。AnimatorListenerAdapter提供了实现了Animator.AnimatorListener()**接口但是提供了一些空实现的方法。

比如,我们可以仅仅实现一个onAnimationEnd()方法的代码示例:

	ValueAnimator.ofFloat(0f, 100f).apply {
        //设置动画持续时间
        duration = 4000
        repeatCount = 10
        //设置重复次数
        repeatMode = ValueAnimator.REVERSE
        //设置加速插值器
        interpolator = AccelerateInterpolator()
        addUpdateListener { updateAnimation ->
            //改变目标的属性值
            iv_image.translationX = (updateAnimation.animatedValue as Float)
        }
    		//添加一个重要事件的监听
        addListener(object:AnimatorListenerAdapter(){
            override fun onAnimationEnd(animation: Animator?) {
                super.onAnimationEnd(animation)
                iv_image.visibility=View.GONE
            }
        })
        start()
    }

自定义TypeEvaluator

如果安卓系统无法识别你当前想要转换的动画类型值,你可以通过实现TypeEvaluator实现**evaluate()**来自定义动画计算值。安卓系统为int,float和color这些类型提供了支持类IntEvaluator,FloatEvaluator,ArgbEvaluator.

源码FloatEvaluator

在开始自定义TypeEvaluator之前,我们先来看看源码中定义的Float类型的FloatEvaluator:

public class FloatEvaluator implements TypeEvaluator {
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}

FloatEvaluator的实现很简单,它就是根据fraction插值分数,计算当前动画的值。

自定义TypeEvaluator流程

为了更好的理解Property Animation 是如何工作了,我们编写一个抛物线的动画特效:

先编写一个PointFEvaluator自定义TypeEvaluator类:

public class PointFEvaluator implements TypeEvaluator<PointF> {

    @Override
    public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
        PointF pointF=new PointF();
        pointF.x=200 * fraction * 3;
        pointF.y=0.5f * 200 * (fraction * 3) * (fraction * 3);
        return pointF;
    }
}

evaluate()方法中,x轴以x=常量*变量因子改变,y轴以y=常量*变量因子的平方计算,这是抛物线的物理公式。

然后为ValueAnimator设置自定义的TypeEvaluator,并在updateListener监听器中设置属性值。

            ValueAnimator().apply {
                //设置动画持续时间
                duration = 4000
                //设置目标值
                setObjectValues(PointF(0f,0f))
                //设置自定义的 TypeEvalutor
                setEvaluator(PointFEvaluator())
                //设置加速插值器
                interpolator = LinearInterpolator()
                addUpdateListener { updateAnimation ->
                    //改变目标的属性值
                    val pointF=updateAnimation.animatedValue as PointF
                    iv_image.x=pointF.x
                    iv_image.y=pointF.y

                }
                start()
            }

使用Interpolators

Google官网-属性动画

鸿洋-Android 属性动画(Property Animation)

你可能感兴趣的:(android开发)