Android 动画基础 : 属性动画

前言

属性动画是API 11 新加入的特性,和View 动画不同,它对作用对象进行了拓展,属性动画可以对任何对象做动画,甚至可以没有对象。 属性动画可以对任意对象的属性进行动画而不仅仅是View,动画默认时间间隔300ms,默认帧率10ms/帧。其可以达到的效果是:在一个时间间隔内完成对象从一个属性值到另一个属性值的改变。采用开源动画库nineoldandroids 兼容API 11 以前的系统。

比较常用的几个动画类是:ValueAnimatorObjectAnimatorAnimatorSet,其中ObjectAnimator继承自ValueAnimator,AnimatorSet是动画集合,可以定义一组动画。

ObjectAnimator

ObjectAnimator是属性动画框架中最重要的实行类,创建一个ObjectAnimator只需通过他的静态工厂类直接返回一个ObjectAnimator对象。参数包括: 一个对象和对象的属性名字,但这个属性必须有get和set函数,内部会通过Java反射机制来调用set函数修改对象属性值。也可以调用setInterpolator 设置相应的差值器。

  • 平移动画
val transObj=ObjectAnimator.ofFloat(btn,"translationY",btn.height.toFloat())
transObj.repeatCount=10
transObj.start()

效果:
在这里插入图片描述

  • 透明度动画
val colorAnim=ObjectAnimator.ofInt(btn,"backgroundColor",0xFFFF8080.toInt(),0xFF8080FF.toInt())
colorAnim.duration=3000
colorAnim.repeatMode=ValueAnimator.REVERSE
colorAnim.repeatCount=ValueAnimator.INFINITE
colorAnim.setEvaluator(ArgbEvaluator())
colorAnim.start()

效果:
在这里插入图片描述

AnimatorSet

val set=AnimatorSet()
val colorAnim=ObjectAnimator.ofInt(btn,"backgroundColor",0xFFFF8080.toInt(),0xFF8080FF.toInt(),0xFFFF8080.toInt())

val rotateAnimatorX=ObjectAnimator.ofFloat(btn,"rotationX",0f,360f,0f)
val rotateAnimatorY=ObjectAnimator.ofFloat(btn,"rotationY",0f,180f,0f)

val translationAnimatorX=ObjectAnimator.ofFloat(btn,"translationX",0f,90f,0f)
val translationAnimatorY=ObjectAnimator.ofFloat(btn,"translationY",0f,90f,0f)

val scaleAnimatorX=ObjectAnimator.ofFloat(btn,"scaleX",1f,1.5f,1f)
val scaleAnimatorY=ObjectAnimator.ofFloat(btn,"scaleY",1f,0.5f,1f)

val alpha=ObjectAnimator.ofFloat(btn,"alpha",1f,0.25f,1f)
//使用playTogether
set.playTogether(rotateAnimatorX,rotateAnimatorY,translationAnimatorX,translationAnimatorY,scaleAnimatorX,scaleAnimatorY,alpha,colorAnim)
// 使用 after()、before()、with() 方法
set.play(colorAnim).with(rotateAnimatorY).with(translationAnimatorY).with(alpha).after(scaleAnimatorY)

set.duration=5000 
set.reverse()
set.start()

效果 :
使用playTogether效果
在这里插入图片描述
使用 after()、before()、with() 方法效果:
在这里插入图片描述

方法
  • playTogether(Animator… items)
    set集合中各种动画效果同时执行
  • after(Animator anim)
    将现有动画插入到传入的动画之后执行
  • after(long delay)
    将现有动画延迟指定毫秒后执行
  • before(Animator anim)
    将现有动画插入到传入的动画之前执行
  • with(Animator anim)
    将现有动画和传入的动画同时执行

PropertyValuesHolder

在属性动画中,如果针对同一个对象的多个属性,要同时作用多种动画,可以使用PropertyValuesHolder来实现

 val pvh1 = PropertyValuesHolder.ofFloat("translationX", 300f)
 val pvh2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0.1f)
 val pvh3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0.1f)
 ObjectAnimator.ofPropertyValuesHolder(button, pvh1, pvh2, pvh3).setDuration(1000).start()

效果:
在这里插入图片描述

ValueAnimator

ValueAnimator 本身不作用于任何对象,也就是说直接使用它没有任何动画效果。它可以对一个值做动画,然后可以监听其动画过程,在动画过程中修改我们对象的属性值,这样也就相当于我们的对象做了动画。

数字滚动 例子:

private fun valueAnimEx(btn: Button){
    
    
        val anim=ValueAnimator.ofFloat(0f, 100f)
        anim.addUpdateListener {
    
     animator->
            btn.text="¥${
      
      animator.animatedValue}"
        }
        anim.duration=3000
        anim.interpolator=LinearInterpolator()
        anim.start()
    }

效果:
在这里插入图片描述

对任意属性做动画

属性动画动画的原理: 属性动画要求动画作用的对象提供该属性的get 和set 方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值不一样,确切来说,随着时间的推移,所传递的值越来越接近最终值。

情景: 给Button 加一个动画,让这个Button的宽度增加到指定的宽度

解决方法

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

第一种方案多数情况下不可行,除非前提是有权限的话,此种不再赘述。下面第二和第三方案

 private fun performAnimate(target: Button,start :Int,end :Int){
    
    
       // 采用ValueAnimator,监听动画过程,自己实现属性的改变
        val valueAnimator=ValueAnimator.ofInt(1,100)
        val mEvaluator=IntEvaluator()

        valueAnimator.addUpdateListener {
    
    animator->
            val fraction=animator.animatedFraction
            target.layoutParams.width=mEvaluator.evaluate(fraction,start,end)
            target.requestLayout()
        }

        valueAnimator.duration=5000
        valueAnimator.start()

        // 用一个类来包装原始对象,间接为其提供set 和get方法
        val viewWrapper=ViewWrapper(target)
        val  objectAnimator=ObjectAnimator.ofInt(viewWrapper,"width",start,end)
        objectAnimator.duration=5000
        objectAnimator.start()
    }
   inner class ViewWrapper{
    
    
        private var viewTarget: View?=null

        constructor(viewTarget: View?) {
    
    
           this.viewTarget = viewTarget
        }

        fun setWidth(width :Int){
    
    
            viewTarget?.layoutParams?.width=width
            viewTarget?.requestLayout()
        }

        fun getWidth():Int?{
    
    
            return viewTarget?.layoutParams?.width
        }
    }

效果:
在这里插入图片描述

相关API

  • 属性动画监听器
public static interface AnimatorListener {
    
    
 
     void onAnimationStart(Animator animation);
     
     void onAnimationEnd(Animator animation);
     
     void onAnimationCancel(Animator animation);
     
     void onAnimationRepeat(Animator animation);
 }
 
 public static interface AnimatorUpdateListener {
    
    
 
     void onAnimationUpdate(ValueAnimator animation);
 }
  • Xml 布局属性
 // set标签 对应 AnimatorSet
<set xmlns:android="http://schemas.android.com/apk/res/android"
    //together 表示动画集合中子动画同时播放
    //sequentially 表示动画集合中子动画 按照前后顺序依次播放
    android:ordering=["together"|"sequentially"]>

   // animator标签 对应 ValueAnimator
    <animator
        // 表示动画时长
        android:duration="int"
        //表示属性动画作用对象的属性的名称
        android:propertyName="string" 
        // 表示动画的重复 0默认值,-1逆向重复
        android:repeatCount="int"
         // 表示动画的重复模式 reverse逆向重复,restart连续重复
        android:repeatMode=["reverse"|"restart"]
        // 动画延时的时间
        android:startOffset="int"
        // 属性的起始值
        android:valueFrom="float|color|int"
        // 属性的结束值
        android:valueTo="float|color|int"
        // 表示android:propertyName所指定的属性的类型,如果所指定的属性值是颜色
        // 那么不需要指android:valueType,系统会自动对颜色类型的属性做处理
        android:valueType=["intType"|"floatType"]/>

   // objectAnimator标签 对应 ObjectAnimator
    <objectAnimator
        android:duration="int"
        android:propertyName="string"
        android:repeatCount="int"
        android:repeatMode=
        android:startOffset="int"["reverse"|"restart"]
        android:valueFrom="float|color|int"
        android:valueTo="float|color|int"
        android:valueType=["intType"|"floatType"] />
</set>

猜你喜欢

转载自blog.csdn.net/xufei5789651/article/details/113316163