文章目录
一、补间动画(Tweens)介绍
在两个关键帧中间需要做“补间动画”,才能实现图画的运动;插入补间动画后两个关键帧之间的插补帧是由计算机自动运算而得到的。——《百度百科》
根据上一章,我们可以知道,在Android中逐帧动画其实就是一个AnimationDrawable对象,对应着xml中的 <animation-list>,本质是个Drawable。
而补间动画呢,对应的就是Animation了(直接剧透),我们翻翻Animation这个类,查看他的继承关系:
根据类的名字我们就可以划分不同补间动画的功能:
- TranslateAnimation 位移动画
- AlphaAnimation 透明动画
- ScaleAnimation 缩放动画
- RotateAnimation 旋转动画
- AnimationSet 组合动画
二、实现
不同于逐帧动画,补间动画的实现必须与View相关联,可以通过View.startAnimation(Animation anim)或者直接View.setAnimation(Animation anim)来开始动画。
0、准备
我们先简单的创建一个View:
<View
android:id="@+id/tweenView"
android:layout_width="200dp"
android:layout_height="100dp"
android:background="#447DC2"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
创建目录:
右键res目录 => new => 点击Android Resource Directory =>
创建anim目录。该目录专门存放补间动画的动画资源。
1、位移(TranslateAnimation)
对于第一个动画例子,我会描述的更详细一点,一些准备性或者共同性的东西,我只在此描述一遍,以免赘述。
- 在anim目录新建一个Animation Resource File,在Root element填上translate。
- 填写一些动画的属性:
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="100"
android:toYDelta="100"
android:duration="1000">
</translate>
以View的左上角的点为准:
fromXDelta 和 fromYDelta 代表View的第一帧起始位置,
toXDelta 和 toYDelta 代表View的终点帧的位置。
注意:fromXDelta、fromYDelta、toXDelta 和 toYDelta的值都是相对于View自己的位置。且都是以View的左上角的点为准。
- 通过AnimationUtils加载xml动画资源。
val anim = AnimationUtils.loadAnimation(context,R.anim.anim_translate)
//fillAfter用于设置是否回停留在动画完播放毕后位置。
//如果设置为false,那么动画播放完后,View又会回到起始位置。
anim.fillAfter = true
btn_start.setOnClickListener {
tweenView.startAnimation(anim)
}
那么这个View就会在自己起始的位置,向右移动100的距离,向下移动100的距离(为了更好的展示效果,我在开发者设置中打开了显示布局边界):
我们再把fromXDelta 和 fromYDelta不设置为0,设置成50看看:
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="50"
android:fromYDelta="50"
android:toXDelta="100"
android:toYDelta="100"
android:duration="1000">
</translate>
果然是直接“闪到”(50,50)的位置,然后再向(100,100)的位置前进。
1.1、通过代码实现
以上是通过创建xml动画资源和AnimationUtils生成的动画实例,其实我们还可以用代码来实现这个功能:
//我们先看看TranslateAnimation的构造方法,其中有一个是这样的:
public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
//那么我们就可以这样实现:
val transAnim = TranslateAnimation(0f,100f,0f,100f)
transAnim.duration = 1000
transAnim.fillAfter = true
btn_start.setOnClickListener {
tweenView.startAnimation(transAnim)
}
这样实现的效果和之前实现的效果是一模一样的。
1.2、两者区别
那么这两种有什么区别嘛?
通过xml的话,可以更加灵活,我们可以在代码中写一个方法,方法参数带有一个ResId来指向某个动画资源的id,我们只需要替换id来达到切换不同动画的效果。
2、透明(AlphaAnimation)
和之前一样,先创建一个xml动画资源,将Root element填写为alpha:
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="0"
android:toAlpha="1"
android:duration="1000">
</alpha>
对于透明度的控制就这两个属性,fromAlpha 和 toAlpha,从哪个透明度到哪个透明度,其值为0到1之间,0是完全透明,1是完全不透明。
然后在代码中加载动画:
val anim = AnimationUtils.loadAnimation(context,R.anim.anim_alpha)
anim.fillAfter = true
//代码实现 其构造方法:
//public AlphaAnimation(float fromAlpha, float toAlpha)
//val alphaAnim = AlphaAnimation(0f,1f)
//alphaAnim.duration = 1000
//alphaAnim.fillAfter = true
btn_start.setOnClickListener {
tweenView.startAnimation(anim)
//tweenView.startAnimation(alphaAnim)
}
3、缩放(ScaleAnimation )
创建一个xml动画资源,将Root element填写为scale:
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXScale="200dp"
android:fromYScale="100dp"
android:toXScale="100dp"
android:toYScale="50dp"
android:pivotX="200"
android:pivotY="100"
android:duration="1000">
</scale>
- fromXScale 和 fromYScale 代表“第一帧”View的宽和高
- toXScale 和 toYScale 代表“最后帧”View的宽和高
- pivotX 和 pivotY 表示一个点,这个点是缩放的轴心。
注意:fromXScale 等四个属性是可以设置比例的,其值为不带单位的整数值;
而pivotX 和 pivotY两个属性也是可以设置比例的,其值为带%的值。
所以为了更好的适配屏幕和各种View,我们应该使用带比例的值:
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXScale="1"
android:fromYScale="1"
android:toXScale="0.5"
android:toYScale="0.5"
android:pivotX="50%"
android:pivotY="30%"
android:duration="1000">
</scale>
代码:
val anim = AnimationUtils.loadAnimation(context,R.anim.anim_scale)
anim.fillAfter = true
//代码实现 其构造方法:
//public ScaleAnimation(float fromX, float toX, float fromY, float toY,
// int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
//前面四个参数没什么好讲的,pivotXValue 和 pivotYValue 就是一个float类型的百分比。
//pivotXType 和 pivotYType 有三个取值:ScaleAnimation.ABSLUTE 、
//ScaleAnimation.RELATIVE_TO_SELF和ScaleAnimation.RELATIVE_TO_PARENT
//第一个表示绝对位置,取值为float类型,后面两个分别表示以自身View或者
//父View作为参照对象,取值为0-1的float值。
//val scaleAnim = ScaleAnimation(1f,0.5f,1f,0.5f,
// ScaleAnimation.RELATIVE_TO_SELF,0.5f,ScaleAnimation.RELATIVE_TO_SELF,0.3f)
//scaleAnim.duration = 1000
//scaleAnim.fillAfter = true
btn_start.setOnClickListener {
tweenView.startAnimation(anim)
//tweenView.startAnimation(scaleAnim)
}
3.1、关于pivot
直接以一张图作为解释:
注意:此图仅作pivotType为ScaleAnimation.RELATIVE_TO_SELF时的解释。
4、旋转(RotateAnimation)
创建一个xml动画资源,将Root element填写为rotate:
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="90"
android:pivotY="50%"
android:pivotX="50%"
android:duration="1000">
</rotate>
- formDegress “第一帧”的角度
- toDegrees “最后帧”的角度
- pivotX 和 pivotY 表示旋转中心
关于角度:
- 当角度为0的时候,View的显示是正常的。
- 往顺时针方向旋转为正值。
- 往逆时针方向旋转为负值
- 一圈是360度。
代码:
val anim = AnimationUtils.loadAnimation(context,R.anim.anim_rotate)
anim.fillAfter = true
//代码实现 其构造方法:
//public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
// int pivotYType, float pivotYValue)
//val rotateAnim = RotateAnimation(0f,90f,
// RotateAnimation.RELATIVE_TO_SELF,0.5f,RotateAnimation.RELATIVE_TO_SELF,0.5f)
//rotateAnim.duration = 1000
//rotateAnim.fillAfter = true
btn_start.setOnClickListener {
tweenView.startAnimation(anim)
//tweenView.startAnimation(rotateAnim)
}
5、组合动画(AnimationSet)
组合动画顾名思义,就是将上面四种动画组合到一起。
创建一个xml动画资源,将Root element填写为set:
这里Demo只展示两种动画的组合,在实际过程中,你想怎么组合都行。
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000">
<rotate
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="90" />
<alpha
android:fromAlpha="0"
android:toAlpha="1" />
</set>
val anim = AnimationUtils.loadAnimation(context,R.anim.anim_set)
anim.fillAfter = true
//代码实现 直接new一个AnimationSet,其构造方法里的Boolean值
//代表是否共用一个插值器。
//val animSet = AnimationSet(true)
//创建两个动画,然后添加
//animSet.addAnimation(anim1)
//animSet.addAnimation(anim2)
btn_start.setOnClickListener {
tweenView.startAnimation(anim)
//tweenView.startAnimation(animSet)
}
三、常用方法
- setRepeatCount(int repeatCount) 动画循环播放repeatCount+1次,当repeatCount==-1(Animation.INFINITE)时,动画将无限循环。
- setRepeatMode(int repeatMode) 设置循环类型,共有两种:Animation.RESTART 和 Animation.REVERSE。
两者区别:
- setInterpolator(Interpolator i) 设置一个“插值器”,用于控制动画速率。
- setStartOffset(long startOffset) 延迟 startOffset 毫秒开始动画。
- cancel() 停止动画。
四、注意事项
- 设置动画后,如果动画是有次数的播放(一次或者多次,不包含循环播放),待到播放完毕,View.getAnimation()=null,所以想利用View.getAnimation().start()来重新开始动画会报错!
- 在上面的动画成果展示的时候,你们可以明显的发现,只是画面有变化,而布局并没有发生变化,所以其点击事件的位置并没有变。
五、优缺点
优点
- 相对于逐帧动画,用户的交互性提升了一点点。
- 实现简单。
- 资源占用较小。
缺点
- 布局不能随动画的变化而变化。
- 能实现的效果有限。
六、扩展
我用补间动画做了个小Demo:
感兴趣的小伙伴可以戳这里。