当前位置: 首页 > news >正文

Android笔记:动画

文章目录

  • 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"><translateandroid:toXDelta="500" />
</set>
class MainActivity : AppCompatActivity() {lateinit var btn: Buttonlateinit var text_1: TextViewlateinit var text_2: TextViewlateinit var text_3: TextViewlateinit var text_4: TextViewlateinit var text_5: TextViewlateinit var text_6: TextViewlateinit var text_7: TextViewlateinit var text_8: TextViewlateinit var text_9: TextViewlateinit var text_10: TextViewlateinit var text_11: TextViewlateinit var text_12: TextViewoverride 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 = 2000alphaAnim.fillAfter = truetext_1.startAnimation(alphaAnim)// 缩放val scaleAnim: ScaleAnimation = ScaleAnimation(1f,1.5f,1f,1.5f)scaleAnim.duration = 2000scaleAnim.fillAfter = truetext_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 = 2000translateAnim.fillAfter = truetext_3.startAnimation(translateAnim)// 旋转val rotateAnim: RotateAnimation = RotateAnimation(0f, 135f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)rotateAnim.duration = 2000rotateAnim.fillAfter = truetext_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"><itemandroid:drawable="@drawable/aaa_1"android:duration="100"/><itemandroid:drawable="@drawable/aaa_2"android:duration="100"/><itemandroid: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:backgroundanimation.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 = 2000translateAnim.fillAfter = truetext_1.startAnimation(translateAnim)var valueAmim: ValueAnimator = ValueAnimator.ofInt(0, 200)valueAmim.duration = 2000val 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.Interpolatorclass 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.FloatingActionButtonandroid: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"/><Buttonandroid: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"/><Buttonandroid: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"/><Buttonandroid: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"/><Buttonandroid: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.sinclass MainActivity : AppCompatActivity() {lateinit var btn_1: Buttonlateinit var btn_2: Buttonlateinit var btn_3: Buttonlateinit var btn_4: Buttonprivate lateinit var floatingActionButton: FloatingActionButtonprivate var isOpen: Boolean = falseoverride 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 = timeanimatorSet.start()}
}

相关文章:

Android笔记:动画

文章目录1.View Animation&#xff08;视图动画&#xff09;1.1 Tween Animation&#xff08;补间动画&#xff09;Animation 继承属性透明度alpha缩放scale移动translate旋转rotateset标签Animation父类共有函数1.2Frame Animation &#xff08;逐帧动画&#xff09;2.Propert…...

Git学习总结

目录 Git工作的基本流程图 git基本配置 配置SSH公钥 查看提交日志&#xff08;log&#xff09; 版本回退 为常用指令配置别名 添加文件至忽略列表 Git操作的基本指令 ​编辑 Git远程仓库的操作 把黑马的Git视频看完了黑马程序员Git全套教程&#xff0c;完整的git项目管…...

第四天笔记

1. 简述自定义转换器的使用过程&#xff1f; 第一步&#xff1a;定义一个类&#xff0c;实现 Converter 接口&#xff0c;该接口有两个泛型。 第二步&#xff1a;在 spring配置文件中配置类型转换器。  Spring配置类型转换器的机制是 将自定义的转换器注册到类型转换服务中去…...

《MySQL学习》 全局锁和表锁

一.MySQL锁的分类 二.全局锁 全局锁对整个数据库加锁&#xff0c;可以执行如下命令&#xff0c;整个数据库都将处于只读状态。 Flush tables with read lock ;我们可以执行 unlock table进行解锁 unlock table ;读操作 非读操作&#xff08;阻塞&#xff09; 全局锁的典型使…...

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;可以根据自己板子形状确定 导…...

用VSCode搭建Vue.js开发环境及Vue.js第一个应用

目录 一、VSCode安装 二、VSCode简单配置 三、Vue.js的下载和引入 四、Vue.js第一个应用 一、VSCode安装 Visual Studio Code是一个轻量级但功能强大的源代码编辑器&#xff0c;可在您的桌面上运行&#xff0c;可用于Windows&#xff0c;macOS和Linux。它内置了对JavaScrip…...

Leetcode 每日一题 2341. 数组能形成多少数对

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…...

前后端分离-小项目-3前后端交互

第一步编写前端页面&#xff0c;第二步搭建后端环境&#xff0c;现在开始第三步&#xff0c;继续完善前端功能完善“添加”按钮功能Ajax异步请求安装在前端项目安装ajax。在Terminal输入&#xff1a;npm i axios -S点击“添加”按钮&#xff0c;弹出Dialog对话框设置对话框里面…...

Spring如何整合MyBatis框架?使用XML及java类的配置方式

前言 Spring文章链接: 从头到尾Spring概念&#xff0c;家族&#xff0c;核心技术使用&#xff0c;事务这一篇就够了&#xff01;&#xff01;&#xff01;_千小半的博客-CSDN博客_spring最新技术 mybatis文章链接: MyBatis框架入门(含实例)_mybatis sqlsession创建和关闭_千小…...

第七届蓝桥杯省赛——8冰雹数(递归)

题目&#xff1a;任意给定一个正整数N&#xff0c;如果是偶数&#xff0c;执行&#xff1a; N / 2如果是奇数&#xff0c;执行&#xff1a; N * 3 1生成的新的数字再执行同样的动作&#xff0c;循环往复。通过观察发现&#xff0c;这个数字会一会儿上升到很高&#xff0c;一会…...

Android 10.0 设置静态ip重启后获取不到ip的修复

1.概述 在定制化开发中,对于设置静态ip以后可以正常使用,但是遇到一个新问题 就是开机以后,获取不到ip 地址,这就有点不正常了,获取不到ip 就自然连不上网了,所以要分析问题所在解决问题 2.设置静态ip重启后获取不到ip的修复的核心代码 frameworks/opt/net/ethernet/java…...

mysql笔记

基础 概念 数据库体系结构的三级模式为&#xff1a;外模式、概念模式和内模式。 内模式&#xff1a;存储模式&#xff0c;对数据的物理结构和存储方式的描述。提供数据定义语言定义的。如顺序还是索引存储&#xff08;将概念模式定义的数据进行组织存储&#xff0c;达到较好…...

华为OD机试 - 最多等和不相交连续子序列(Python)| 真题+思路+考点+代码+岗位

最多等和不相交连续子序列 题目 给定一个数组,我们称其中连续的元素为连续子序列,称这些元素的和为连续子序列的和。 数组中可能存在几组连续子序列,组内的连续子序列互不相交且有相同的和。 求一组连续子序列,组内子序列的数目最多。输出这个数目。 输入 第一行输入为…...

第四届宁波网安市赛训练题

Crypto 散乱的密文 8fd4a4c94gf15{50}l72d3提示了2 1 6 5 3 4&#xff0c;我们直接横向排列 2165348fd4a4c94gf15{50}l72d3 按顺序竖着抄下来fc1l84f}a45dg034{2d957,然后栅栏解密&#xff0c;注意这里是W型栅栏解密&#xff0c;行数6 flag:flag{52048c453d794df1} 综合解密…...

Windows中MySQL 8.x版本忘记密码如何重设

Windows中MySQL 8.x版本忘记密码如何重置 文章目录Windows中MySQL 8.x版本忘记密码如何重置一、前言二、重置密码操作1、停止MySQL服务2、以安全模式启动MySQL服务3、无密码登录mysql4、重置登录密码5、验证密码是否重置成功三、最后我想说一、前言 好久之前在电脑下载的MySQL…...

【信管12.1】信息文档管理与配置管理

信息文档管理与配置管理对于项目管理来说&#xff0c;文档非常重要&#xff0c;如果是传统的工程行业项目的话&#xff0c;仅仅标书就是几百上千页的。相对来说&#xff0c;其实信息系统开发项目已经好很多了。另外就是配置项&#xff0c;它是比文档更大的一个概念&#xff0c;…...

一文搞懂Linux的标准输出/错误重定向

前言 今天在写一个脚本时&#xff0c;需要将shell命令和可执行程序的输出重定向在某一个log文件中&#xff0c;但是遇到了点小问题&#xff0c;索性就研究下输出重定向到底怎么回事。 Linux系统&#xff0c;有一个非常重要概念&#xff0c;就是一切皆文件。在使用shell脚本时&a…...

【OJ】计数的梦

&#x1f4da;Description: Bessie 处于半梦半醒的状态。过了一会儿&#xff0c;她意识到她好像在数羊&#xff0c;不能入睡。Bessie的大脑反应灵敏&#xff0c;仿佛真实地看到了她数过的一个又一个数。她开始注意每一个数码&#xff1a;每一个数码在计数的过程中出现过多少次…...

【项目实战】MySQL使用CONCAT字符串拼接函数实现与特殊字符的拼接

一、需求说明 因为有新功能需要上生产环境&#xff0c;总有一些乱七八糟的兼容历史数据的活要去做&#xff0c;比如以下。 需要批量的更新数据库中某个字段&#xff08;如id列中原来是ABCDEFG&#xff0c;需要改成[“ABCDEFG”]&#xff09;&#xff0c; 没错&#xff0c;就是…...

OpenCV实战(11)——形态学变换详解

OpenCV实战&#xff08;11&#xff09;——形态学变换详解0. 前言1. 腐蚀和膨胀运算1.1 腐蚀和膨胀基础1.2 使用形态学滤波器执行图像腐蚀和膨胀运算2. 开运算和闭运算2.1 使用形态学滤波器执行图像开运算和闭运算3. 形态学变换应用3.1 使用形态学滤波器检测边缘3.2 使用形态学…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

Caliper 配置文件解析:fisco-bcos.json

config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像&#xff08;比如分辨率3000*3000的图像&#xff09;的办法&#xff0c;尤其是想把内存中的裸数据&#xff08;只有图像的数据&#xff0c;不包…...