Android笔记:动画

news2025/3/11 17:01:36

文章目录

  • 1.View Animation(视图动画)
    • 1.1 Tween Animation(补间动画)
        • Animation 继承属性
        • 透明度alpha
        • 缩放scale
        • 移动translate
        • 旋转rotate
        • set标签
        • Animation父类共有函数
    • 1.2Frame Animation (逐帧动画)
  • 2.Property Animation(属性动画)
    • 2.1 ValueAnimator
        • 常用方法
        • 监听器
        • 自定义插值器 和 Evaluator
    • 2.2 ObjectAnimator
        • 基本使用方法
        • 自定义ObjectAnimator
    • 2.3 动画组合AnimatorSet
  • 3例子

动画分为两种类型: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例子

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

<?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()
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/349163.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spark3.0源码分析-driver-executor心跳机制

前言 driver和executor心跳机制分为两种机制&#xff1a; 1、executor发送心跳机制 2、driver接受心跳机制 至于为何要分为两种&#xff0c;原因是在分布式场景中&#xff0c;服务的稳定性是无法保障的&#xff0c;例如executor宕机后无法发送心跳&#xff0c;故driver端需要…

3、按键扫描检测处理

说明&#xff1a;本文处理按键的短按、长按检测执行&#xff0c;非矩阵按键 硬件可以类似如下连接即可&#xff0c;无需放置上下拉电阻&#xff1b; 按键动作分长按、短按(弹起时执行)两种 按下不放执行长按&#xff0c;但松开按键时不予执行短按函数 多个按键可以同时操作 按…

内网渗透(三十四)之横向移动篇-IPC配合计划任务横向移动

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

Altium Designer输出生产文件Gerber、IPC、NC Drill、坐标文件--AD

AD软件版本&#xff1a;22.2.1 gerber文件输出共有两部分&#xff1a; 1、Gerber Files:铜皮 和 外形分别导出 2、Nc Drill Files 分3次导出 一、Gerber Files 导出2次 设定原点 ** Edit->Origin->Set** 一般板边左下角为原点&#xff0c;可以根据自己板子形状确定 导…

使用MAT工具分析OOM问题

1、添加jvm参数 保存堆内存快照 -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath存放路径 2、下载MAT工具 下载地址&#xff1a; https://www.eclipse.org/mat/downloads.php 3、启动如果遇到 Version 1.8.0 102 of the JVM is not suitable for this product, Version: 11…

2023LaCTFWriteup

文章目录2023LaCTFEBEa hackers notes2023LaCTF 前言&#xff1a; 累了&#xff0c;没有话&#xff0c;下次一定 EBE I was trying to send a flag to my friend over UDP, one character at a time, but it got corrupted! I think someone else was messing around with me…

【C++】类与对象(三)

前言 本章我们接替前一章继续深入理解类的默认成员函数&#xff0c;赋值重载&#xff0c;取地址重载&#xff0c;及const取地址操作符重载 但是在讲剩下的三个默认成员函数之前&#xff0c;我们要先来了解运算符重载&#xff0c;因为赋值重载&#xff0c;取地址重载&#xff0c…

Compose-Navigation简单案例上手

Navigation 快速上手 下面案例简要展示使用 Compose 版本的 Navigation 库来实现两个页面之间的跳转 这是完整的结构&#xff08;忽略掉红线划过的那个包&#xff09; 安装适用于 kotlin 的 navigation 依赖 dependencies {implementation("androidx.navigation:navigati…

兼职任务平台收集(一)分享给有需要的朋友们

互联网时代&#xff0c;给人们带来了很大的便利。信息交流、生活缴费、足不出户购物、便捷出行、线上医疗、线上教育等等很多。可以说&#xff0c;网络的时代会一直存在着。很多人也在互联网上赚到了第一桶金&#xff0c;这跟他们的努力和付出是息息相关的。所谓一份耕耘&#…

使用kubeadm部署k8s1.24.0版本,遇到的坑总结

使用kubeadm部署k8s1.24.0版本&#xff0c;遇到的坑总结环境安装遇到的问题环境 操作系统&#xff1a;centos7 内核&#xff1a;5.4.231-1.el7.elrepo.x86_64 kubeadm&#xff1a;1.24.0 kubelet&#xff1a;1.24.0 kubectl&#xff1a;1.24.0 cri&#xff1a;docker cni&…

nginx rewrite 规则

生效位置 rewrite 可以写在 server 、 location、 if 配置段内 格式 rewrite regex replacement [flag] regex是匹配的正则表达式。 只会处理$request_uri部分&#xff0c;不会匹配$host replacement 是跳转的URI。 可以以http:// 或者https:// 开头&#xff0c; 也…

自适应多因素认证:构建不可破解的企业安全防线|身份云研究院

打开本文意味着你理解信息安全的重要性&#xff0c;并且希望获取行业最佳实践来保护你所在组织的信息安全。本文将带你了解多因素认证&#xff08;MFA&#xff1a;Multi-Factor-Authentication&#xff09;对于企业信息安全的重要性以及实施方法。 多因素认证&#xff08;MFA&…

尚医通(十五)医院排班管理

目录一、医院排班管理需求1、页面效果2、接口分析二、科室列表&#xff08;接口&#xff09;1、添加service接口和实现2、添加DepartmentController方法三、科室列表&#xff08;前端&#xff09;1、添加隐藏路由2、封装api方法3、添加/views/hosp/schedule.vue组件四、排班日期…

torch相比于numpy都干了什么——torch入门

Torch已深度学习框架被熟知&#xff0c;但它首先是作为Numpy的存在。我们首先比较一下Torch和Numpy有什么不同&#xff0c;为什么可以实现深度学习。 从数据结构看起。Numpy的强大之处就在于array的数据结构&#xff0c;它是多维数组&#xff0c;要求所有元素是相同类型的&…

人与人之间赚钱的差距在哪里呢?体现在这几个因素中

同样生而为人&#xff0c;同样接受九年制义务教育的熏陶&#xff0c;但最终赚钱能力却千差万别&#xff0c;因此也就形成了我们所谓的圈层&#xff0c;阶层&#xff0c;穷人和富人。 一个人的赚钱能力跟什么有关&#xff1f;资源技能、学历、认知&#xff0c;这些都会决定一个人…

搞清楚动态库和静态库的区别

什么是库文件 一般来说&#xff0c;一个程序&#xff0c;通常都会包含目标文件和若干个库文件。经过汇编得到的目标文件再经过和库文件的链接&#xff0c;就能构成可执行文件。库文件像是一个代码仓库或代码组件的集合&#xff0c;为目标文件提供可直接使用的变量、函数、类等…

visual studio 改变界面语言

在使用visual studio 2019 时&#xff0c;开始是英文界面&#xff0c;后面变成了中文界面。但是看视频教学时有的是英文界面&#xff0c;我就想回到英文界面&#xff0c;所以有切换界面语言的需要。其实操作很简单&#xff1a;工具-> 选项 打开界面在界面里选择环境&#xf…

axios中的resolvePromise为什么影响promise状态

axios的取消请求意思很简单&#xff0c;就是在发送请求后不久停止发送请求 本文探讨的是v0.22.0之前的CancelToken API&#xff0c;因为在阅读源码交流的时候发现很多朋友不理解为什么CancelToken中的resolvePromise会影响到实例对象身上的promise状态 即下图所示代码&#xf…

为乡村振兴塑形铸魂,VR全景助力美丽乡村建设

民族要复兴&#xff0c;乡村必振兴&#xff0c;文旅部一直推动乡村文化产业和乡村旅游融合发展&#xff0c;大力推进乡村文化数字化&#xff0c;为乡村文化振兴注入新动能。为了更好的给乡村振兴塑形铸魂&#xff0c;VR智慧乡村助力数字化乡村建设发展&#xff0c;利用VR全景展…

CMD SET % 字符串截取

:: 使用 CSDN Cobol 格式化风格。:: *. 百分号 (%) :: (1) % 作为变量引导符&#xff0c;有几种用途&#xff1a;在命令行窗口中&#xff0c;用 1 个 % 引号变量; 在批处理文件中&#xff0c;用 2 个 % 引号变量。[1] 两个 % 夹着一个变量名称&#xff0c;表示取&#xff08;扩…