文章目录
- 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()
}
}