Android动画全篇系列(四)——属性动画

一、属性动画(Animator)介绍

属性动画系统是一个强健的框架,用于为几乎任何内容添加动画效果。——亲爸爸谷歌

我们知道,在Android中,补间动画对应的类为Animation,而属性动画对应的类则为Animator

属性动画是Android 3.0才引入的,相对于补间动画是比较新的存在。

属性动画顾名思义,是指改变某个属性,来达到实现动画的目的。例如,我想改变View的透明度,用补间动画的做法,就是创建一个AlphaAnimation,然后交给View开始动画,而属性动画则直接操控View的alpha属性,在一个时间段内,变化alpha的值,来使得View的透明度发生变化。

这里放出Animator的继承关系图,现在脑海里留个印象:
继承图

二、ValueAnimator

属性动画的主计时引擎,它也可计算要添加动画效果的属性的值。它具有计算动画值所需的所有核心功能,同时包含每个动画的计时详情、有关动画是否重复播放的信息、用于接收更新事件的监听器以及设置待评估自定义类型的功能。——亲爸爸谷歌

就以透明动画为例,我们使用ValueAnimator来创建这样一个动画。

1、创建ValueAnimator实例

这边直接new就行了。

val anim = ValueAnimator()
2、设置动画的相关属性

作为一个动画,肯定有播放时长吧:

anim.duration = 1000

作为一个ValueAnimator,肯定有Value吧!
呆滞
根据谷歌爸爸提供的信息:“它也可计算要添加动画效果的属性的值”。
那么我们就要添加动画效果的属性的值……

回到出发点,我们是要改变View的透明度,那么就是设置他的透明度属性,那透明度属性的值肯定就是我们需要添加的!
机灵
View.setAlpha(float value),里面的value取值范围0到1,
0就是完全透明,1就是完全不透明。那还等什么,赶紧设置值吧,高兴的我赶紧找到anim.setFloatValue()方法,又呆滞了:

public void setFloatValues(float... values) 

这个方法里面的参数是可变参数啊,那么我们到底要传什么值呢?
这里直接揭露吧:

  • 如果只传一个值,那么动画会默认从0到这个值的过程中,计算生成一直在变化的值。
  • 如果传两个值,那么这段时间,动画会计算生成从第一个值到第二个值的过程中,一直在变化的值。
  • 传入多个值,那么就会分多个阶段计算,得到计算之后的值。
3、值已经设置好了,队长,下一步怎么办?
anim.setFloatValues(0f,1f)

接下来就是将anim和View“关联起来”:

		anim.addUpdateListener {valueAnimator->
            animatorView.alpha = valueAnimator.animatedValue as Float
        }

通过对动画设置“更新监听”,使得每一次更新的时候,拿到其animatedValue就是计算后的值,在本例中这个值就是从0到1之间的数。

完整代码如下

    val anim = ValueAnimator()
    anim.setFloatValues(0f,1f)
    anim.duration = 1000
    anim.addUpdateListener {valueAnimator->
        animatorView.alpha = valueAnimator.animatedValue as Float
    }
    btn_start.setOnClickListener {
        anim.start()
    }

结果

4、改进
  1. 事实上,ValueAnimator给我们提供了多个静态方法,来帮助我们直接创建带有值的实例:
val anim = ValueAnimator.ofFloat(0f,1f)
  1. 我们记得不管是逐帧动画还是补间动画,都可以定义xml动画资源,那么属性动画也是可以的。需要在res创建animator目录,然后在其目录中创建xml资源文件。
    ValueAnimator 对应的xml标签为 <animator>,如下所示:
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:valueFrom="0"
    android:valueTo="1"
    android:valueType="floatType"
    android:duration="1000">
</animator>

然后通过AnimatorInflater.loadAnimator()方法加载资源文件

   val animFromXML = AnimatorInflater.loadAnimator(context,R.animator.anim_alpha) as ValueAnimator
   animFromXML.addUpdateListener {
       animatorView.alpha = it.animatedValue as Float
   }

三、ObjectAnimator

ValueAnimator 的子类,用于设置目标对象和对象属性以添加动画效果。此类会在计算出动画的新值后相应地更新属性。——亲爸爸谷歌

我们还是先从它的实现开始吧。还是以透明动画为例,我们使用ObjectAnimator来创建这样一个动画。

1、创建ObjectAnimator实例
	val objectAnimator = ObjectAnimator()
	//顺便把时间设置好
    objectAnimator.duration = 1000
2、设置属性

根据谷歌的文档,作为一个ObjectAnimator,需要设一个目标对象目标对象的属性,设置目标对象很少理解,直接就是我们需要展示动画的View:

 	objectAnimator.target = animatorView
    objectAnimator.setPropertyName("alpha")

可是目标属性呢?

要添加动画效果的对象属性必须具有 set<PropertyName>() 形式的 setter 函数(采用驼峰式大小写形式)。——亲爸爸谷歌

好在View的设置透明度的方法符合这样的要求:setAlpha(),那么PropertyName就是“Alpha”了:

objectAnimator.setPropertyName("Alpha")

最后设置值的范围:

objectAnimator.setFloatValues(0f,1f)

原所有代码:

    val objectAnimator = ObjectAnimator()
    objectAnimator.duration = 1000
    objectAnimator.target = animatorView
    objectAnimator.setPropertyName("Alpha")
    objectAnimator.setFloatValues(0f,1f)
    
    btn_start.setOnClickListener {
        objectAnimator.start()
    }

效果就不展示了,跟之前的一模一样。

3、怎样的属性才能设置?(重点)

前面也说了,我们必须遵守其规则,标准的setter方法,必须采用驼峰命名法!

  1. 注意:如果我们设置value的时候,只设置一个value,它就会认定为这个值是结束值,那么初始值就会通过getter方法来获取。如果没有这个getter方法的话,否则out。
  2. 注意:如果有用到getter方法的话,其返回值的类型必须setter方法参数类型一致,否则out。
  3. 如果你的Target对象没有setter或者getter,很囧怎么办?
    解决办法:创建一个ViewHolder类,这个类持有这个View,然后手动添加settergetter
  class ViewHolder(val view:View){

        fun setPropertyName(value:Float){
            view.alpha = value
        }

        fun getPropertyName(): Float {
            return view.alpha
        }
    }

	val viewHolder= ViewHolder(animatorView)
        //ObjectAnimator
    val objectAnimator = ObjectAnimator.ofObject(viewHolder,"PropertyName",FloatEvaluator(),0f)
  1. 别耍小聪明~不支持Kotlin扩展方法。
4、改进
  1. ObjectAnimator也提供了多个静态方法,来一次性初始化若干个动画属性:
    val objectAnimator = 
    ObjectAnimator.ofObject(animatorView,"Alpha",FloatEvaluator(),0f,1f)

我们需要添加一个Evaluator对象,这是一个估值器,下一章会有介绍。

在这里我们传入的Value类型是Float类型的,所以就传入FloatEvaluator,如果是int类型,就传入IntEvaluator,这些基本的估值器系统都有提供。

  1. 我们同样也能够生命xml的ObjectAnimator动画资源文件,其对应的xml标签为 <objectAnimator>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
   
    android:propertyName="Alpha"
    android:valueFrom="0"
    android:valueTo="1"
    android:valueType="floatType"
    android:duration="1000">
</objectAnimator>

然后在代码中初始化,注意添加target对象:

    val objectFromXml = AnimatorInflater.loadAnimator(context,R.animator.anim_object) as ObjectAnimator
    objectFromXml.target = animatorView

四、AnimatorSet

在许多情况下,您需要根据一个动画开始或结束的时间来播放另一个动画。借助 Android 系统,您可以将动画捆绑到一个 AnimatorSet 中,以便指定是同时播放动画、按顺序播放还是在指定的延迟时间后播放。您还可以相互嵌套 AnimatorSet 对象。——亲爸爸谷歌

1、方法一

上面这一段话已经描述的很清楚了,我们直接看如何实现:

	//假设有两个Animator anim1和anim2
	val animatorSet = AnimatorSet()
	
    //直接播放 anim1
    animatorSet.play(anim1)
    
    //播放完 anim1 紧接着播放 anim2
    animatorSet.play(anim1).after(anim2)

	//在播放anim1 之前先播放 anim2
	animatorSet.play(anim1).before(anim2)

	//同时播放 anim1 和 anim2
	animatorSet.play(anim1).with(anim2)

	//播放完 anim1 等过了1000ms之后再播放 anim2
	animatorSet.play(anim1).after(1000).after(anim2)

	//同时播放 anim1 和 anim2
	animatorSet.playTogether(anim1,anim2)

	//最后最后,需要调用 start()方法开始动画
	animatorSet.start()
2、方法二

当然,我们依然可以定义一个AnimatorSet的xml动画资源,其对应的xml标签为 <set>:

<set xmlns:android="http://schemas.android.com/apk/res/android">

    <objectAnimator
        android:propertyName="alpha"
        android:valueFrom="0"
        android:valueTo="1"
        android:valueType="floatType"
        android:duration="1000" />

    <objectAnimator
        android:propertyName="TranslationX"
        android:valueFrom="0"
        android:valueTo="100"
        android:valueType="floatType"
        android:duration="1000"/>

</set>

然后再代码中初始化:

	val setFromXml = AnimatorInflater.loadAnimator(context,R.animator.anim_set) as AnimatorSet
    setFromXml.setTarget(animatorView)

效果如图所示:
效果

3、注意点
  1. <set> 里面可以设置 <animator><objectAnimator><set> 三个子标签。
  2. 在被 <set> 标签包裹的动画,表示会同时播放。
  3. 在构建 <set> 的时候,尽量不要使用 <animator>,因为你还需要设置其更新监听,然后再在里面添加方法。在上面例子中,如果将第一个 <objectAnimator> 改成 <animator>,那么你还需要在代码中添加如下代码:
	val anim1 = setFromXml.childAnimations[0] as ValueAnimator
	anim1.addUpdateListener { valueAnimator
            animatorView.alpha = valueAnimator.animatedValue as Float
        }

这样的话得不偿失,反而更加麻烦。

五、动画监听

在实际项目中,我们可能需要在动画的某个节点来做某些事情,熟悉他们的监听时很有必要的事情。

1、Animator

Animator作为超类,它拥有的两个监听:

  • Animator.AnimatorListener
    – onAnimationStart() - 在动画开始播放时调用。
    – onAnimationEnd() - 在动画结束播放时调用。
    – onAnimationRepeat() - 在动画重复播放时调用。
    – onAnimationCancel() - 在动画取消播放时调用。
    取消的动画也会调用onAnimationEnd(),无论它们以何种方式结束。
  • Animator.AnimatorPauseListener
    – onAnimationPause() - 在动画暂停时调用。
    – onAnimationResume() - 在动画暂停之后,继续播放时调用。
2、ValueAnimator

ValueAnimator自身只有一个监听:

  • ValueAnimator.AnimatorUpdateListener
    – onAnimationUpdate() - 对动画的每一帧调用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值