当前位置: 首页 > 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 使用形态学…...

SPI协议详解(Standard SPI、Dual SPI和Queued SPI)

1、标准SPI 1.1、SPI接口的引脚 (1)SCLK&#xff1a;时钟线&#xff1b; (2)MOSI(master output slave input)&#xff1a;主设备输出&#xff0c;从设备输入&#xff0c;单向传输&#xff1b; (3)MISO(master input slave output)&#xff1a;主设备输入&#xff0c;从设备输…...

【代码随想录二刷】Day15-二叉树-C++

代码随想录二刷Day15 今日任务 层序遍历 226.翻转二叉树 101.对称二叉树 语言&#xff1a;C 层序遍历 102.二叉树的层序遍历 class Solution { public:vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> res;if(root NULL) …...

C++为什么能重夺年度语言?

目录一、爷青回1、年初依旧很多大新闻&#xff0c;其中一条就是TIOBE把年度编程语言颁给了C。2、这是什么概念&#xff1f;那一年Java的流行指数是14%。二、C为什么衰落三、C为什么重新流行1、C为什么重新流行起来了呢&#xff1f;2、C究竟做对了什么呢&#xff1f;3、根本原因…...

视频监控实时接入——以海康威视为例(2023.2.16)

海康威视实时视频监控接入学习 2023.2.16引言1、视频协议简介1.1 RTSP——Real Time Streaming Protocol&#xff08;实时流传输协议&#xff09;1.2 RTMP——Real Time Messaging Protocol&#xff08;实时消息传输协议&#xff09;1.3 HLS——HTTP Live Streaming&#xff08…...

推荐系统[一]:超详细知识介绍,一份完整的入门指南,解答推荐系统是什么。

1. 推荐算法的初步理解 如果说互联网的目标就是连接一切,那么推荐系统的作用就是建立更加有效率的连接,推荐系统可以更有效率的连接用户与内容和服务,节约了大量的时间和成本。 1.1 推荐系统主要解决问题 任务一:挖掘长尾:帮助用户找到想要的物品(音乐、商品、新闻),…...

新手小白入门必看!如何批量注册Twitter账号?

Twitter是目前海外比较流行的社媒营销平台&#xff0c;所以很多从事跨境电商行业的朋友都需要利用多个Twitter账号来推广营销&#xff0c;但是注册和管理多个Twitter账号其实并不是简单的事情。龙哥将会在这里详细讲讲该如何批量注册并且让这些账号不会因为关联被封号&#xff…...

虚拟环境的创建以及labelme的使用教程

本来打算是将这两部分分开的&#xff0c;但写完虚拟环境的创建似乎字数太少了&#xff0c;不过二者有关联&#xff0c;所以就放一起了。简单介绍一下&#xff0c;虚拟环境的创建有win11系统已经Ubuntu系统&#xff0c;labelme教程包括了下载及其使用的全部流程&#xff0c;以及…...

CSS中的BFC详细讲解(易懂)

带你用最简单的方式理解最全面的BFC~~~1.先了解最常见定位方案普通流元素按照其在 HTML 中的先后位置至上而下布局行内元素水平排列&#xff0c;直到当行被占满然后换行&#xff0c;块级元素则会被渲染为完整的一个新行所有元素默认都是普通流定位浮动元素首先按照普通流的位置…...

华为3面,官网显示面试通过了...开始泡池子,进入漫长等待期

背景&#xff1a; 现在双非本科&#xff0c;非计算机科班&#xff0c;有算法方面的奖&#xff0c;有嵌入式开发经历&#xff0c;官网显示面试通过&#xff0c;短信说录用情况在十个工作日内告知&#xff0c;看别人的说法应该是泡池子了。 全程视频面试&#xff0c;一天面完三…...

【新2023】华为OD机试 - 构成的正方形数量(Python)

构成的正方形数量 题目 输入 N 个互不相同的二维整数坐标, 求这 N 个坐标可以构成的正方形数量。(内积为零的两个向量垂直) 输入 第一行输入为 N,N 代表坐标数量,N为正整数。N <= 100 之后的 K 行输入为坐标 x y以空格分隔,x, y 为整数, -10 <= x, y <= 10 输…...

三级分销网站建设报价方案/seo代做

这篇文章主要介绍了python批量提取图片信息并保存的实现&#xff0c;文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值&#xff0c;需要的朋友们下面随着小编来一起学习学习吧程序运行环境code# -*- coding:utf-8 -*-# ----------------…...

90设计网站真实稿费/如何做免费网站推广

在真正的生产大数据环境中&#xff0c;存储管理是DBA的主要工作之一&#xff0c;比如生产系统上表空间的监控&#xff0c;当一个表空间满或者使用率过高时&#xff0c;可能需要DBA添加一块磁盘到操作系统中&#xff0c;然后再在新加的磁盘上添加数据文件。如果这个操作作用的环…...

遵义网站建设厂家/竞价系统

<wbr> 1、什么是const?<br> 常类型是指使用类型修饰符const说明的类型&#xff0c;常类型的变量或对象的值是不能被更新的。&#xff08;当然&#xff0c;我们可以偷梁换柱进行更新&#xff1a;&#xff09;</wbr> 2、为什么引入const&#xff1f;   cons…...

网站设计与网页制作毕业论文/黄金网站app视频播放画质选择

现在数据库或是某个文件里有一些数据需要显示在界面上该怎么办呢&#xff1f;我们的目的就是将这些数据设法放到界面上去可以显示出来&#xff0c;这个将数据放到界面上去的过程可以叫住数据绑定。在Flex应用中程序中&#xff0c;数据绑定的实际是借助事件机制来完成的&#xf…...

卖网站怎样做/传播易广告投放平台

这也许是一个让粉丝略伤感的新闻&#xff0c;《Halo2》PC多人游戏服务器将在下个月永久关闭。343 Industries注意到服务器的峰值在线人数一直仅有20人&#xff0c;因此做出了关闭服务器的决定。查看原图343 Industries在Halo Waypoint中说道&#xff1a;“我们很遗憾地宣布&…...

上海高端室内设计事务所/seo排名查询软件

插件的安装如下&#xff1a;1.下载插件包https://github.com/vim-scripts/Pydiction可以直接下载&#xff0c;也可git下载[rootlocalhost]# git clone https://github.com/rkulla/pydiction.git#####################包括三个文件python_pydiction.vim #vim插件complete-di…...