Android 补间动画 浅析

什么是补间动画

百度百科的解释:在两个关键帧中间需要做“补间动画”,才能实现图画的运动;插入补间动画后两个关键帧之间的插补帧是由计算机自动运算而得到的

在Android 中 则是 通过使用 Animation 对单张图片执行一系列转换来创建动画,通过对图形执行旋转、淡出、移动和拉伸等转换,达到动画的效果

补间动画的分类

补间动画可细分成5大类

类型含义对应 Animation 子类对应的 xml 属性
Translate平移动画TranslateAnimationtranslate
Scale缩放动画ScaleAnimationscale
Rotate旋转动画RotateAnimationrotate
Alpha透明度动画AlphaAnimationalpha
Set组合动画AnimationSetset

如何使用补间动画

补间动画的使用方式有两种方式 xml 和 代码 动态设置

xml 方式

需要在 res/anim 文件夹下面 创建对应的 静态动画资源,
在使用 AnimationUtils.loadAnimation(this, 资源ID) 的方式获取到 Animation 对象,
最后使用view.startAnimation(anim)的方式设置给需要添加动画效果的View

代码设置

使用 Animation 的继承类,如TranslateAnimation 等,设置需要的属性
最后使用view.startAnimation(anim)的方式设置给需要添加动画效果的View

透明度动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 btn_alpha.setOnClickListener {

// 方式1 代码设置
/**
* 参数 1 fromAlpha 动画开始时的透明度
* 参数 2 toAlpha 动画结束时的透明度
*/
val anim = AlphaAnimation(1f, 0f)
anim.duration = 1000
view_anim.startAnimation(anim)

// 方式2 xml 设置
val animation = AnimationUtils.loadAnimation(this, R.anim.alpha)
view_anim_2.startAnimation(animation)
}
1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromAlpha="1.0"
android:toAlpha="0.0">
</alpha>
参数含义
toAlpha动画结束时的透明度
fromAlpha动画开始时的透明度

旋转动画

1
2
3
4
5
6
7
8
9
10
11
12
 //旋转
btn_rotate.setOnClickListener {
// 方式1 代码设置
val anim = RotateAnimation(0f, 360f,0f,0f)
anim.duration = 1000
view_anim.startAnimation(anim)


// 方式2 xml 设置
val animation = AnimationUtils.loadAnimation(this, R.anim.rotate)
view_anim_2.startAnimation(animation)
}
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromDegrees="0"
android:pivotX="0"
android:pivotY="0"
android:toDegrees="360">

</rotate>
参数含义
fromDegrees起始角度
fromDegrees结束角度
pivotX起始X坐标
pivotY结束Y坐标

缩放动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

//缩放
btn_scale.setOnClickListener {
// 方式1 代码设置
val anim = ScaleAnimation(
1.0f, 0f,
1.0f, 0f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f)
anim.duration = 1000
view_anim.startAnimation(anim)

// 方式2 xml 设置
val animation = AnimationUtils.loadAnimation(this, R.anim.scale)
view_anim_2.startAnimation(animation)
}
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromYScale="1.0"
android:toYScale="0.0"
android:fromXScale="1.0"
android:toXScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
>

</scale>
参数含义
fromXScale起始 X 缩放比例
toXScale结束 X 缩放比例
fromYScale起始 Y 缩放比例
toYScale结束 Y 缩放比例
pivotX图片做缩放运动时候中心点 X 的位置
pivotY图片做缩放运动时候中心点 Y 的位置

平移动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//平移
btn_translate.setOnClickListener {

// 方式1 代码设置
val anim = TranslateAnimation(
Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0.5f,
Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0f
)
anim.duration = 1000
view_anim.startAnimation(anim)

// 方式2 xml 设置
val animation = AnimationUtils.loadAnimation(this, R.anim.translate)
view_anim_2.startAnimation(animation)
}
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXDelta="0"
android:toXDelta="50%p"
android:fromYDelta="0"
android:toYDelta="0"
>
</translate>
参数含义
fromXDelta起始 X 偏移
toXDelta结束 X 偏移
fromYDelta起始 Y 偏移
toYDelta结束 Y 偏移

组合动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 //组合动画
btn_set.setOnClickListener {
// 方式1 代码设置
//true : 所有子元素中共用同一插值器
val anim = AnimationSet(true)
anim.addAnimation(
TranslateAnimation(
Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0.5f,
Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0f
)
)
anim.addAnimation(AlphaAnimation(1f, 0f))
anim.duration = 1000
view_anim.startAnimation(anim)

// 方式2 xml 设置
val animation = AnimationUtils.loadAnimation(this, R.anim.set)
view_anim_2.startAnimation(animation)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="1000"
android:fromAlpha="1.0"
android:toAlpha="0.0" />

<translate
android:duration="1000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="50%p"
android:toYDelta="0" />

</set>

50%p 和 50% 区别

在上面的示例中 我们设置平移动画的时候有一个 50%p

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXDelta="0"
android:toXDelta="50%p"
android:fromYDelta="0"
android:toYDelta="0"
>
</translate>

这个 android:toXDelta="50%p" 代表着 以 当前View 的左上角 为原点,向右移动 父类控件宽度的 50%

如果给他改成 android:toXDelta="50%" 则表示 向右移动自身宽度的 50%

当前也可以直接写上数字,代表,移动多少

RELATIVE_TO_PARENT 解析

在使用 平移动画 的时候 我们注意到有一个 参数 Animation.RELATIVE_TO_PARENT

1
2
3
4
TranslateAnimation(
Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0.5f,
Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0f
)

作用和 xml 中使用的 50%p 相同

  • Animation.ABSOLUTE(默认值) 相对于控件的0点的坐标值
  • Animation.RELATIVE_TO_SELF 相对于自己宽或者高的百分比(1.0表示100%)
  • Animation.RELATIVE_TO_PARENT 相对于父控件的宽或者高的百分比.

监听器

使用 setAnimationListener 方法 即可添加监视器

1
public void setAnimationListener(AnimationListener listener)

下面是对 Translate 进行添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

val anim = TranslateAnimation(
Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0.7f,
Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0f
)
anim.repeatCount = ValueAnimator.INFINITE
anim.repeatMode = ValueAnimator.REVERSE
anim.duration = 2000
anim.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation?) {
view_anim.text = "补间动画开始"
}

override fun onAnimationEnd(animation: Animation?) {
view_anim.text = "补间动画结束"
}

override fun onAnimationRepeat(animation: Animation?) {
view_anim.text = "补间动画重复"
}
})
view_anim.startAnimation(anim)


//点击view触发 cancel 方法
view_anim.setOnClickListener {
anim.cancel()
}

当点击View 原来的位置的时候,才会触发点击事件,这也是 补间动画的特性