Android笔记:动画


动画分为两种类型:View Animation(视图动画)和 Property Animation(属性动画),其中 View Animation 包括 Tween Animation(补间动画)和 Frame Animation (逐帧动画),Property Animation 包括 ValueAnimator 和 ObjectAnimator

1.View Animation(视图动画)

1.1 Tween Animation(补间动画)

由5中类型组成 alpha、scale、translate、rotate、set

Animation 继承属性

android:duration:完成一次动画的时间,毫秒
android:fillAfter:true,控件动画结束时,保持动画结束时的状态
android:fillBefore:true,控件动画结束时,还原到初始化状态
android:fillEnabled:与 fillBefore 相同,控件动画结束时,还原到初始化状态
android:repeatCount:动画重复的次数,为 infintie 时表示无线循环,设置为1表示重复一次,即播放两边动画
android:repeatMode:动画重复的类型,reverse倒叙回放,restart重放,必须与repeatCount 一起使用才有效果
android:interpolator:指定插值器,比如弹跳效果等

android:interpolator="@android:anim/linear_interpolator" //默认值先线性插值器

不指定插值器动画的效果都是匀速进行,即默认值为 线性(匀速)插值器

说明
LinearInterpolator匀速 默认值
AccelerateInterpolator加速插值器 开始速率为0,后面加速,到结束位置速度最大
DecelerateInterpolator减速插值器 开始速率最大,后面减速,到结束位置速度为0
AccelerateDecelerateInterpolator开始和结束速率较慢,中间快,加速插值器 和 减速插值器 的结合
AnticipateInterpolator初始偏移插值器,动画开始时,向相反的方向移动一段时间,可设置便宜等级,默认为2,越大偏移越明显
OvershootInterpolator结束偏移插值器,动画结束时,延续动画运行一段时间,然后在回到结束位置
AnticipateOvershootInterpolator初始结束偏移插值器,初始偏移插值器 和 结束偏移插值器 的结合
BounceInterpolator弹跳插值器 类似玻璃弹珠掉到地上的效果,自由落体后的回弹
CycleInterpolator循环正弦插值器,进行一次正弦波运动,cycles 表示循环次数
LinearOutSlowInInterpolator匀速减速插值器,先匀速在减速,类似Decelerate,过程不全是减速,有一段匀速
FastOutLinearInInterpolator加速匀速插值器,先加速在匀速,Accelerate
FastOutSlowInInterpolator加速减速插值器,先加速后减速,类似 AccelerateDecelerate

在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true"
    android:duration="3000"
    android:interpolator="@android:anim/linear_interpolator">
    <translate
        android:toXDelta="500" />
</set>
class MainActivity : AppCompatActivity() {

    lateinit var btn: Button
    lateinit var text_1: TextView
    lateinit var text_2: TextView
    lateinit var text_3: TextView
    lateinit var text_4: TextView
    lateinit var text_5: TextView
    lateinit var text_6: TextView
    lateinit var text_7: TextView
    lateinit var text_8: TextView
    lateinit var text_9: TextView
    lateinit var text_10: TextView
    lateinit var text_11: TextView
    lateinit var text_12: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btn = findViewById<Button>(R.id.btn)
        text_1 = findViewById<TextView>(R.id.text_1)
        text_2 = findViewById<TextView>(R.id.text_2)
        text_3 = findViewById<TextView>(R.id.text_3)
        text_4 = findViewById<TextView>(R.id.text_4)
        text_5 = findViewById<TextView>(R.id.text_5)
        text_6 = findViewById<TextView>(R.id.text_6)
        text_7 = findViewById<TextView>(R.id.text_7)
        text_8 = findViewById<TextView>(R.id.text_8)
        text_9 = findViewById<TextView>(R.id.text_9)
        text_10 = findViewById<TextView>(R.id.text_10)
        text_11 = findViewById<TextView>(R.id.text_11)
        text_12 = findViewById<TextView>(R.id.text_12)


        btn.setOnClickListener(View.OnClickListener {
        	//默认匀速
            val translateAnim: Animation = AnimationUtils.loadAnimation(this, R.anim.my_translate)
            text_1.startAnimation(translateAnim)

            //加速插值器
            val translateAnim2: Animation = AnimationUtils.loadAnimation(this, R.anim.my_translate)
            translateAnim2.interpolator = AccelerateInterpolator()
            text_2.startAnimation(translateAnim2)

            //减速插值器
            val translateAnim3: Animation = AnimationUtils.loadAnimation(this, R.anim.my_translate)
            translateAnim3.interpolator = DecelerateInterpolator()
            text_3.startAnimation(translateAnim3)

            //加速减速插值器
            val translateAnim4: Animation = AnimationUtils.loadAnimation(this, R.anim.my_translate)
            translateAnim4.interpolator = AccelerateDecelerateInterpolator()
            text_4.startAnimation(translateAnim4)

            //初始偏移插值器
            val translateAnim5: Animation = AnimationUtils.loadAnimation(this, R.anim.my_translate)
            translateAnim5.interpolator = AnticipateInterpolator(2f)
            text_5.startAnimation(translateAnim5)

            //结束偏移插值器
            val translateAnim6: Animation = AnimationUtils.loadAnimation(this, R.anim.my_translate)
            translateAnim6.interpolator = OvershootInterpolator(2f)
            text_6.startAnimation(translateAnim6)

            //初始结束偏移插值器
            val translateAnim7: Animation = AnimationUtils.loadAnimation(this, R.anim.my_translate)
            translateAnim7.interpolator = AnticipateOvershootInterpolator(2f)
            text_7.startAnimation(translateAnim7)

            //弹跳插值器
            val translateAnim8: Animation = AnimationUtils.loadAnimation(this, R.anim.my_translate)
            translateAnim8.interpolator = BounceInterpolator()
            text_8.startAnimation(translateAnim8)

            //循环正弦插值器
            val translateAnim9: Animation = AnimationUtils.loadAnimation(this, R.anim.my_translate)
            translateAnim9.interpolator = CycleInterpolator(1f)	//cycles 表示循环次数
            text_9.startAnimation(translateAnim9)

            //匀速减速插值器
            val translateAnim10: Animation = AnimationUtils.loadAnimation(this, R.anim.my_translate)
            translateAnim10.interpolator = LinearOutSlowInInterpolator()
            text_10.startAnimation(translateAnim10)

            //加速匀速插值器
            val translateAnim11: Animation = AnimationUtils.loadAnimation(this, R.anim.my_translate)
            translateAnim11.interpolator = FastOutLinearInInterpolator()
            text_11.startAnimation(translateAnim11)

            //加速减速插值器
            val translateAnim12: Animation = AnimationUtils.loadAnimation(this, R.anim.my_translate)
            translateAnim12.interpolator = FastOutSlowInInterpolator()
            text_12.startAnimation(translateAnim12)
        })
    }
}
透明度alpha

android:fromAlpha:动画开始的透明度,0.0~1.0,0.0全透明,1.0完全不透明
android:toAlpha:动画结束的透明度,同上
使用 java 代码方式设置动画

AlphaAnimation(Context context, AttributeSet attrs)		//用于从本地 XML 文件中加载动画
AlphaAnimation(float fromAlpha, float toAlpha)

// 调用
view.startAnimation(new AlphaAnimation(0.0, 1.0))
缩放scale

android:fromXScale:动画起始时,控件在X轴方向相对于自身的缩放比例,浮点值,1.0 表示自身无变化,0.5 表示缩小到原来的二分之一,2.0表示放大到原来的两倍
android:toXScale:动画结束时,控件在Y轴方向相对于自身的缩放比例
android:fromYScale:同上,动画起始时,在Y轴方向
android:toYScale:同上,动画结束时,在Y轴方向
android:pivotX:缩放起始点X坐标,可以是数值,百分比,百分比p(50、50%、50%p)数值表示:以控件左上角为原点,加上50px的位置,百分比表示:以控件左上角为原点,加上 控件宽度 ∗ * 百分比px 的位置,百分比p表示:以控件左上角为原点,加上 控件父控件的宽度 ∗ * 百分比 的位置
android:pivotY:同上,缩放起始点Y坐标

ScaleAnimation(Context context, AttributeSet attrs)
ScaleAnimation(float fromX, float toX, float fromY, float toY)
ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY)
ScaleAnimation(float fromX, float toX, float fromY, float toY, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
// 标签属性 android:pivotX/Y 中有三种取值样式,分别是数值、百分数、百分数p ,体现在构造函数中 pivotXType 参数
//Animation.ABSOLUTE 				数值
//Animation.RELATIVE_TO_SELF		百分比
//Animation RELATIVE_TO_PARENT		百分比p
移动translate

android:fromXDelta:起始点X轴坐标,可以是数值,百分数,百分数p
android:fromYDelta:起始点Y轴坐标
android:toXDelta:终点X轴坐标
android:toYDelta:终点Y轴坐标

TranslateAnimation(Context context, AttributeSet attrs)
TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue, int fromYType, float fromYValue, int toYType, float toYValue)
旋转rotate

android:fromDegress:动画开始旋转时的角度位置,正值代表顺时针方向,负值代表逆时针方向
android:toDegress:动画结束旋转时的角度位置,同上
android:pivotX:旋转中心点X坐标,同上
android:pivotY:旋转中心点Y坐标,同上

RotateAnimation(Context context, AttributeSet attrs)
RotateAnimation(float fromDegrees, float toDegrees)
RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY)
RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
set标签

set 标签是一个容器类,定义动画集,前面4个只能完成特定的动画,set可以将这些动画组合起来,共同完成一个动画,注意:在set 标签中设置 repeateCount 是无效的,必须对每个动画单独设置才有效

AnimationSet(Context context, AttributeSet attrs)
AnimationSet(boolean shareInterpolator)
//shareInterpolator 为true 时表示,AnimationSet类中定义一个插值器( Interpolator ),其下面的所有动画共用该插值器,使用setInterpolator(Interpolator i)设置插值器
//shareInterpolator 为false 时表示,其下面的所有动画各自定义插值器

// 使用下面方法添加动画
addAnimation(Animation a)

在这里插入图片描述

	override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btn = findViewById<Button>(R.id.btn)
        text_1 = findViewById<TextView>(R.id.text_1)
        text_2 = findViewById<TextView>(R.id.text_2)
        text_3 = findViewById<TextView>(R.id.text_3)
        text_4 = findViewById<TextView>(R.id.text_4)

        btn.setOnClickListener(View.OnClickListener {
            // 透明
            val alphaAnim: AlphaAnimation = AlphaAnimation(1f, 0.3f)
            alphaAnim.duration = 2000
            alphaAnim.fillAfter = true
            text_1.startAnimation(alphaAnim)

            // 缩放
            val scaleAnim: ScaleAnimation = ScaleAnimation(1f,1.5f,1f,1.5f)
            scaleAnim.duration = 2000
            scaleAnim.fillAfter = true
            text_2.startAnimation(scaleAnim)

            // 平移
            val translateAnim: TranslateAnimation = TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF,2f, Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f)
            translateAnim.duration = 2000
            translateAnim.fillAfter = true
            text_3.startAnimation(translateAnim)

            // 旋转
            val rotateAnim: RotateAnimation = RotateAnimation(0f, 135f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
            rotateAnim.duration = 2000
            rotateAnim.fillAfter = true
            text_4.startAnimation(rotateAnim)

        })
    }
Animation父类共有函数
cancel()		//取消动画
reset()			//将控件重置到动画开始前的状态
setAnimationListener(Animation.AnimationListener listener)	//设置监听回调函数


	animation.setAnimationListener(object : Animation.AnimationListener{
            override fun onAnimationStart(animation: Animation?) {	//动画开始时
                TODO("Not yet implemented")
            }

            override fun onAnimationEnd(animation: Animation?) {	//动画结束时
                TODO("Not yet implemented")
            }

            override fun onAnimationRepeat(animation: Animation?) {	//动画重复时
                TODO("Not yet implemented")
            }
        })

1.2Frame Animation (逐帧动画)

像电影一样,一帧一帧播放图片,要放在 res/drawable 目录下

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:oneshot="true"
    tools:ignore="MissingDefaultResource">
    <item
        android:drawable="@drawable/aaa_1"
        android:duration="100"/>
    <item
        android:drawable="@drawable/aaa_2"
        android:duration="100"/>
    <item
        android:drawable="@drawable/aaa_3"
        android:duration="100"/>
</animation-list>

animation-list 元素是必须的,必须作为根元素,android:oneshot 为 true 表示动画只执行一次,false 表示一直循环
每一个item放一张图片,android:duration 表示图片显示的时间,一个整数单位毫秒
定义好后,可以通过 android:src 属性或者 android:background 属性进行设置
调用

var image: ImageView = findViewById(R.id.myImageView);
var animation: AnimationDrawable = image.drawable as AnimationDrawable		//android:src
//var animation: AnimationDrawable = image.background as AnimationDrawable	//android:background

animation.start()

AnimationDrawable 类
void start():开始播放动画
void stop():停止播放动画
boolean isRunning():动画是否在播放中
int getNumberOfFrames():得到当前 AnimationDrawable的所有帧数量
int getDuration(int i):得到指定帧 i 的持续时间
Drawable getFrame(int index):得到指定 i 的帧所对应的 Drawable 对象
boolean isOneShot():判断 AnimationDrawable 是否执行一次,true 表示执行一次,false 表示循环执行
void setOneShot(boolean oneShot):设置 AnimationDrawable 是否执行一次
void addFrame(@NonNull Drawable frame, int duration):为AnimationDrawable 添加一帧并设置这个帧的持续时间(毫秒)

 		var id:Int = resources.getIdentifier("aaa", "drawable", packageName) // context.getPackageName()
        var drawableId: Drawable = resources.getDrawable(id, null)
        animation.addFrame(drawableId, 60)

方法都比较简单,设置60毫秒一帧

2.Property Animation(属性动画)

属性动画是为了弥补视图动画的不足而设计的,能狗实现补间动画无法实现的功能,视图动画仅能对派生自View类的控件实例起作用,而属性动画是通过改变控件的某一个属性值来做动画
例如:给一个 TextView 添加点击事件,运用视图动画将 TextView 移动到另一个地方,然后点击TextView 控件并没有相应,点击TextView 原来的区域却有反应,而通过属性动画就不会有这个问题

2.1 ValueAnimator

这个动画是针对值的,不会对控件执行任何操作,需要监听它的动画过程来操控控件

常用方法

public static ValueAnimator ofInt(int… values):参数类型是可变参数,传进去的值列表就表示动画时的变化范围,比如(0, 100, 50)表示数字从0变化到 100 再变化到 50
public static ValueAnimator ofFloat(float… values):同上,参数类型不同
public static ValueAnimator ofArgb(int… values):同上
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object… values):同上,后续讲到

public ValueAnimator setDuration(long duration):设置动画时常
public Object getAnimatedValue():获得当前运动点的值
public void start():开始动画
public void cancel():取消动画
public void setRepeatCount(int value):设置循环次数,ValuAnimation.INFINITE 表示无线循环
public void setRepeatMode(@RepeatMode int value):设置循环模式 RESTART | REVERSE
注意:设置 ValuAnimation.INFINITE 无线循环,当 Activity 结束时必须调用 cancel() 方法取消动画,否则动画还在运行导致View 无法释放,导致绑定View的Activity资源也无法释放,从而导致内存泄露
public void setInterpolator(TimeInterpolator value):设置一个插值器
public abstract void setStartDelay(long startDelay):设置延迟多久开始动画(毫秒)
public Animator clone():完全克隆一个ValueAnimator,包括监听器代码的处理

监听器

有两个监听器

	//用于监听动画过程中值的实时变化
	public static interface AnimatorUpdateListener {
        void onAnimationUpdate(ValueAnimator animation);
	}
	// 通过ValueAnimator.addUpdateListener() 方法设置监听器
	public void addUpdateListener(AnimatorUpdateListener listener)
	public void removeUpdateListener(AnimatorUpdateListener listener)	//移除监听器
	public void removeAllUpdateListeners()
	
	/**********************************************************/
	//用于监听 Animation 的4个状态,是ValueAnimator 父类 Animator 类中的监听方法
	public static interface AnimatorListener {
        default void onAnimationStart(Animator animation, boolean isReverse) {
            onAnimationStart(animation);
        }

        default void onAnimationEnd(Animator animation, boolean isReverse) {
            onAnimationEnd(animation);
        }

        void onAnimationStart(Animator animation);	//动画开始时回调

        void onAnimationEnd(Animator animation);	//动画结束时回调

        void onAnimationCancel(Animator animation);	//动画取消时回调,调用cancel方法时回调

        void onAnimationRepeat(Animator animation);	//动画重复时回调
    }
    // 通过 Animator.addListener() 方法设置监听器
	public void addListener(AnimatorListener listener)
	public void removeListener(AnimatorListener listener)
	public void removeAllListeners()
    

在这里插入图片描述

	override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btn = findViewById<Button>(R.id.btn)
        text_1 = findViewById<TextView>(R.id.text_1)
        text_2 = findViewById<TextView>(R.id.text_2)

        text_1.setOnClickListener(View.OnClickListener {
            Toast.makeText(this, "11111", Toast.LENGTH_SHORT).show()
        })
        text_2.setOnClickListener(View.OnClickListener {
            Toast.makeText(this, "22222", Toast.LENGTH_SHORT).show()
        })

        btn.setOnClickListener(View.OnClickListener {

            // 平移
            val translateAnim: TranslateAnimation = TranslateAnimation( 0f, 200f, 0f, 0f)
            translateAnim.duration = 2000
            translateAnim.fillAfter = true
            text_1.startAnimation(translateAnim)

            var valueAmim: ValueAnimator = ValueAnimator.ofInt(0, 200)
            valueAmim.duration = 2000
            val textX = text_2.x.toInt()
            val textY = text_2.y.toInt()
            valueAmim.addUpdateListener {
                val curVale: Int = it.animatedValue as Int	//getAnimatedValue() Object类型
                text_2.layout(textX + curVale, textY, textX + curVale + text_2.width, textY+text_2.height)
            }
            valueAmim.start()

        })
    }

可以看到ValueAnimator是通过控件的 layout() 方法来改变控件位置,layout() 方法改变位置是永久性的,所以第二次运行动画时,控件会继续向右移动,而不会像视图动画跳回初始位置再移动,并且再新的位置可以相应点击事件

自定义插值器 和 Evaluator

与视图动画类似,也可以通过 ValueAnimator.setInterpolator() 方法设置插值器,自定义插值器需要实现 Interpolator 接口
在这里插入图片描述
可以看到最终通过 getInterpolation(float input) 方法来实现不同变化的效果,input:取值范围0~1,表示当前动画进程,0表示开始0%,1表示结束100%,可以理解为时间百分比返回值:实际想要显示的进度,可以超过1或小于0,小于0表示小于开始的位置,可以理解为路程百分比,即,若input为0.2,返回值0.5表示,在动画总时间20%的时候,控件已经移动到了总路程的50%位置,所以上图红框中线性插值器表示:在动画总时间的20%时,控件移动到了总路程的20%,时间是匀速的,所以动画也是匀速的。

import android.view.animation.Interpolator

class Myinterpolator: Interpolator {
    override fun getInterpolation(input: Float): Float {
        return 1 - input
    }
}

设置了上面的自定义插值器,效果就是动画是倒过来的,即从结束的位置运动到开始的位置
Evaluator
就是一个转换器,插值器返回的都是小数(百分比),Evaluator作用就是将这个百分比转换成一个具体的数值返回回去,例如上面AnimatorUpdateListener 监听器中 getAnimatedValue() 方法得到的数值就是 Evaluator 返回的值,ofInt() 函数对应 IntEvaluator ,ofFloat() 函数对应 FloatEvaluator,ofArgb() 函数对应 ArgbEvaluator
在这里插入图片描述
上图可以看到,fraction:插值器返回的路程百分比,startValue,endValue 开始和结束的值,所以

(startInt + fraction * (endValue - startInt))

不难理解所返回的值就是实际的数值,例如startValue=100,endValue =200,在一半的时候就是 100 + (200 - 100) ∗ * 0.5 = 150
可以通过 ValueAnimator.setEvauator() 方法设置自定义的 Evaluator ,而上面的 ofObject() 方法,就需要一个自定义的 Evaluator 来确定返回的对象

2.2 ObjectAnimator

基本使用方法

ObjectAnimator 是 ValueAnimator 的派生类,所以ValueAnimator 中的函数 ObjectAnimator 中都能正常使用,但 ObjectAnimator 中重写了ofInt(),ofFloat()等方法,其他的方法见 ValueAnimator 的常用方法,监听器也是与 ValueAnimator 一样

	override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btn = findViewById<Button>(R.id.btn)
        text_1 = findViewById<TextView>(R.id.text_1)
        text_2 = findViewById<TextView>(R.id.text_2)

        btn.setOnClickListener(View.OnClickListener {

            val objectAnimator: ObjectAnimator  = ObjectAnimator.ofFloat(text_1, "rotationX" , 0f ,360f);
            // kotlin:text_1.rotationX = 270f 
            // java:text_1.setRotationX(270f) 实际为ValueAnimator监听器中设置控件的属性
            objectAnimator.duration = 3000;
            objectAnimator.start();
        })
    }

在这里插入图片描述
效果是沿X轴旋转360°,第二个参数传入一个字符串,是通过反射来调用这个控件的set方法

ObjectAnimator第二个参数的属性说明set方法
alpha透明度setAlpha(float alpha)
translationX沿X轴平移setTranslationX(float x)
translationY沿Y轴平移setTranslationY(float y)
scaleX沿X轴缩放setScaleX(float x)
scaleY沿Y轴缩放setScaleY(float y)
rotationX绕X轴旋转setRotationX(float x)
rotationY绕Y轴旋转setRotationY(float y)
rotation绕Z轴旋转setRotation(float rotation)

注意:通常来说第三个可变参数的数量大于等于2,但如果只传入一个参数,等价于调用get方法获得当前的值,变化到传入的值,如果没有get方法,等价于参数默认的值(int 默认值为0,float 默认值为0.0)变化到传入的值,并且系统会给出警告,但如果参数是用户自定义的,并且没有默认值就会报错闪退

自定义ObjectAnimator

上面说ObjectAnimator是通过反射调用控件的set方法,所以只要控件中含有set方法就能使用ObjectAnimator动画控制,例如自定义控件中有下列set方法

// x轴y轴同时放大或缩小
public void setScaleXY(float scale){
	setRotationX(scale);
	setRotationY(scale);
}
//就可以使用以下,将控件整体放大(x轴y轴同时放大)一倍
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(text_1, "scaleXY" , 0f ,1f);

2.3 动画组合AnimatorSet

注意与视图动画中的 AnimationSet 是不同的,主要有两个方法

public void start()		//开始动画
public void cancel()	//取消动画
public void setDuration(long duration)	//设置时常,注意会覆盖所有的子动画,不设置子动画使用自己的时常
public void setInterpolator(TimeInterpolator value)	//设置插值器,注意会覆盖所有的子动画
public void setTarget(Object target)	//设置动画目标控件,注意会覆盖所有的子动画
public void setStartDelay(long startDelay)	//设置延时播放动画

public void playSequentially(Animator... items) 	//动画会依次播放,注意若上一个动画无限循环,则下一个动画永远不会执行
public void playSequentially(List<Animator> items)

public void playTogether(Animator... items)			//动画会同时播放
public void playTogether(Collection<Animator> items)

AnimatorSet.Builder
但如果想先执行A动画,再将B和C动画一起执行上面的方法是做不到的,需要使用到AnimatorSet.Builder

public Builder play(Animator anim)		//要播放那个动画
public Builder with(Animator anim)		//跟前面一个动画一起播放
public Builder before(Animator anim)	//先播放这个动画再播放前面的动画
public Builder after(Animator anim)		//播放完前面的动画再播放这个动画
public Builder after(long delay)		//延迟 delay 毫秒后开始播放动画

// 下面实现先播放A动画,再B和C动画同时播放
var animatorSet: AnimatorSet = AnimatorSet()
animatorSet.play(animA).after(animB).with(animC)
animatorSet.start()

3.ViewPropertyAnimator

ViewPropertyAnimator 提供了更加易懂、更加面向对象的 API,并且相对于 ObjectAnimator 性能有所提升,但对于程序来说提升的性能微不足道,主要还是简明易读的书写方式

3.1常用方法

函数说明
alpha(value: Float)设置透明度
alphaBy(value: Float)设置透明度增量
scaleX(value: Float)设置X轴方向的缩放大小
scaleXBy(value: Float)设置X轴方向的缩放大小增量
scaleY(value: Float)设置Y轴方向的缩放大小
scaleYBy(value: Float)设置Y轴方向的缩放大小增量
translationX(value: Float)设置X轴方向的移动值
translationXBy(value: Float)设置X轴方向的移动值增量
translationY(value: Float)设置Y轴方向的移动值
translationYBy(value: Float)设置Y轴方向的移动值增量
translationZ(value: Float)设置Z轴方向的移动值
translationZBy(value: Float)设置Z轴方向的移动值增量
rotationX(value: Float)设置绕X轴旋转度数
rotationXBy(value: Float)设置绕X轴旋转度数增量
rotationY(value: Float)设置绕Y轴旋转度数
rotationYBy(value: Float)设置绕Y轴旋转度数增量
rotation(value: Float)设置绕Z轴旋转度数
rotationBy(value: Float)设置绕Z轴旋转度数增量
x(value: Float)相对于父容器的左上角坐标在X轴方向的最终位置
y(value: Float)相对于父容器的左上角坐标在Y轴方向的最终位置
z(value: Float)相对于父容器的左上角坐标在Z轴方向的最终位置
xBy(value: Float)相对于父容器的左上角坐标在X轴方向的最终位置增量
yBy(value: Float)相对于父容器的左上角坐标在Y轴方向的最终位置增量
zBy(value: Float)相对于父容器的左上角坐标在Z轴方向的最终位置增量
start()开始动画
cancel()取消动画
setDuration(duration: Long)设置动画时间
setInterpolator(interpolator: TimeInterpolator!)设置插值器
setStartDelay(startDelay: Long)设置动画延迟开始时间
setListener(listener: Animator.AnimatorListener?)设置监听器
setUpdateListener(listener: ValueAnimator.AnimatorUpdateListener?)设置数值监听器

函数都是上面讲过,不做过多解释

view.animate().alpha(0f);   // 将view的控件透明度从当前数值变为0(完全透明)

4.例子

比较简单不所解释
在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingLeft="50dp"
    android:background="@color/black"
    tools:context=".MainActivity">

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/toolbar_FloatingActionButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:layout_gravity="bottom|end"
        android:layout_marginStart="50dp"
        android:layout_marginTop="50dp"
        android:layout_marginEnd="50dp"
        android:layout_marginBottom="50dp"
        android:visibility="visible"
        app:backgroundTint="#3af5c7"
        />

    <Button
        android:id="@+id/btn_1"
        android:layout_width="45dp"
        android:layout_height="45dp"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:layout_marginStart="55.1dp"
        android:layout_marginTop="55.1dp"
        android:layout_marginEnd="55.1dp"
        android:layout_marginBottom="55.1dp"
        android:background="@drawable/circlel"/>

    <Button
        android:id="@+id/btn_2"
        android:layout_width="45dp"
        android:layout_height="45dp"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:layout_marginStart="55.1dp"
        android:layout_marginTop="55.1dp"
        android:layout_marginEnd="55.1dp"
        android:layout_marginBottom="55.1dp"
        android:background="@drawable/circlel"/>

    <Button
        android:id="@+id/btn_3"
        android:layout_width="45dp"
        android:layout_height="45dp"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:layout_marginStart="55.1dp"
        android:layout_marginTop="55.1dp"
        android:layout_marginEnd="55.1dp"
        android:layout_marginBottom="55.1dp"
        android:background="@drawable/circlel"/>

    <Button
        android:id="@+id/btn_4"
        android:layout_width="45dp"
        android:layout_height="45dp"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:layout_marginStart="55.1dp"
        android:layout_marginTop="55.1dp"
        android:layout_marginEnd="55.1dp"
        android:layout_marginBottom="55.1dp"
        android:background="@drawable/circlel" />
    
</RelativeLayout>
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.floatingactionbutton.FloatingActionButton
import kotlin.math.cos
import kotlin.math.sin

class MainActivity : AppCompatActivity() {

    lateinit var btn_1: Button
    lateinit var btn_2: Button
    lateinit var btn_3: Button
    lateinit var btn_4: Button
    private lateinit var floatingActionButton: FloatingActionButton

    private var isOpen: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        floatingActionButton = findViewById(R.id.toolbar_FloatingActionButton)

        btn_1 = findViewById<Button>(R.id.btn_1)
        btn_2 = findViewById<Button>(R.id.btn_2)
        btn_3 = findViewById<Button>(R.id.btn_3)
        btn_4 = findViewById<Button>(R.id.btn_4)

        btn_1.setOnClickListener(View.OnClickListener {
            Toast.makeText(this, "111111", Toast.LENGTH_SHORT).show()
        })
        btn_2.setOnClickListener(View.OnClickListener {
            Toast.makeText(this, "222222", Toast.LENGTH_SHORT).show()
        })
        btn_3.setOnClickListener(View.OnClickListener {
            Toast.makeText(this, "333333", Toast.LENGTH_SHORT).show()
        })
        btn_4.setOnClickListener(View.OnClickListener {
            Toast.makeText(this, "444444", Toast.LENGTH_SHORT).show()
        })

        floatingActionButton.setOnClickListener(View.OnClickListener {
            if (isOpen){
            	// 关闭动画
                doAnimateOpen(btn_1, 0f, 300, 0, false)
                doAnimateOpen(btn_2, 30f, 300, 100, false)
                doAnimateOpen(btn_3, 60f, 300, 200, false)
                doAnimateOpen(btn_4, 90f, 300, 300, false)
                isOpen = false

            }else{
            	// 打开动画
                doAnimateOpen(btn_1, 0f, 300)
                doAnimateOpen(btn_2, 30f, 300, 100)
                doAnimateOpen(btn_3, 60f, 300, 200)
                doAnimateOpen(btn_4, 90f, 300, 300)
                isOpen = true
            }
        })
    }
	//参数: 控件,角度,半径,延迟时间,是否是打开动画()
    private fun doAnimateOpen(view: View, degree: Float, radius: Int, time: Long = 0, toOpen: Boolean = true){
        if (view.visibility != View.VISIBLE){
            view.visibility = View.VISIBLE
        }
        var degreeRadians = Math.toRadians(degree.toDouble())	// 角度转换为弧度
        var translationX: Float = (radius * sin(degreeRadians)).toFloat()	//注意sin和cos传入的是弧度不是角度
        var translationY: Float = (radius * cos(degreeRadians)).toFloat()

        var animatorSet: AnimatorSet = AnimatorSet()
        if(toOpen){
            animatorSet.playTogether(ObjectAnimator.ofFloat(view, "translationX", 0f, -translationX),
                ObjectAnimator.ofFloat(view, "translationY", 0f, -translationY),
                ObjectAnimator.ofFloat(view, "scaleX", 0f, 1f),
                ObjectAnimator.ofFloat(view, "scaleY", 0f, 1f),
                ObjectAnimator.ofFloat(view, "alpha", 0f, 1f)
            )

        }else{
            animatorSet.playTogether(ObjectAnimator.ofFloat(view, "translationX", -translationX, 0f),
                ObjectAnimator.ofFloat(view, "translationY", -translationY, 0f ),
                ObjectAnimator.ofFloat(view, "scaleX", 1f, 0f),
                ObjectAnimator.ofFloat(view, "scaleY", 1f, 0f),
                ObjectAnimator.ofFloat(view, "alpha", 1f, 0f)
            )
        }

        animatorSet.setDuration(500)
        animatorSet.startDelay = time
        animatorSet.start()
    }
}

5.ViewGroup 内的组件添加动画

前面的动画都是针对于一个控件,如果想对 ViewGroup 内部控件统一入场退场动画,上面的所说的方法是无法做到的,Android 提供了4种方法

5.1 android:animateLayoutChanges 属性

android:animateLayoutChanges=“true | false”,所有派生自 ViewGroup 类的控件,咋添加或移除其中的控件自动添加动画,但不能自定义都是默认的动画

5.2 LayoutTransition

// 所有继承 ViewGroup 都有这个方法,此方法设置入场或出场动画
public void setLayoutTransition(LayoutTransition transition)

// 举例
LayoutTransition layoutTransition = new LayoutTransition();
ObjectAnimator anim = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);
layoutTransition.setAnimator(LayoutTransition.APPEARING, anim);
/*
public roid setAnimator(int transitionType, Animator animator)
int transitionType 表示当前应用动画的对象范围
	LayoutTransition.APPEARING				元素在容器中出现时所定义的动画
	LayoutTransition.DISAPPEARING			元素在容器中消失时所定义的动画
	LayoutTransition.CHANGE_APPEARING		容器中出现一个新元素,其他需要变化的元素所对应的动画
	LayoutTransition.CHANGE_DISAPPEARING	容器中删除某个元素,其他需要变化的元素所对应的动画

*/
viewGroup.setLayoutTransition(layoutTransition); 	//为容器子元素设置动画


/**************LayoutTransition其他方法*******************/
public void setDuration(long duration)
public void setDuration(int transitionType, long duration) //针对单个transitionType设置动画时长
public void setStartDelay(int transitionType, long delay)	//设置动画延迟播放
public void setInterpolator(int transitionType, TimeInterpolator interpolator) //设置插值器
public void setStagger(int transitionType, long duration) //每个子item动画时间间隔
public void addTransitionListener(TransitionListener listener)	//添加监听器,在任何LayoutTransition 开始或结束时回调
public void removeTransitionListener(TransitionListener listener)	//移除监听器

注意:LayoutTransition.CHANGE_APPEARING 和 LayoutTransition.CHANGE_DISAPPEARING 动画必须使用 PropertyValuesHolder 所构造才有效果

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值