动画分类
Android3.0之前提供了两种动画:
- 补间动画 Tween Animation
- 帧动画 Frame Animation
Android3.0之后又增加了新的属性动画(Property Animation)。
按照官方分类,现在Android的动画包括:
- 属性动画 Property Animation
- View动画 View Animation
- 补间动画 Tween Animation
- 帧动画 Frame Animation
因为补间动画和帧动画只能用于View,所以统称为View动画。
补间动画
补间动画主要用于View,实现一些简单的动画效果。
补间动画主要分为四类:
- AlphaAnimation 渐变透明度
- RotateAnimation 画面旋转
- ScaleAnimation 渐变尺寸缩放
- TranslateAnimation 位置移动
每一种动画都可以通过XML或者代码的形式实现,当然,推荐的是XML形式实现。
简单的代码形式
AlphaAnimation
1 | Animation alphaAnimation = new AlphaAnimation(0.1f, 1.0f); |
AlphaAnimation构造函数中:
第一个参数表示起始透明度,第二个参数表示结束透明度。
RotateAnimation
1 | Animation rotateAnimation = new RotateAnimation(0f, 360f); |
RotateAnimation构造函数中:
第一个参数fromDegrees表示动画起始时的角度,第二个参数toDegrees表示动画结束时的角度。
ScaleAnimation
1 | Animation scaleAnimation = new ScaleAnimation(0.1f, 1.0f, 0.1f, 1.0f); |
ScaleAnimation构造函数中:
第一个参数fromX,第二个参数toX:分别是动画起始、结束时X坐标上的伸缩尺寸;
第三个参数fromY,第四个参数toY:分别是动画起始、结束时Y坐标上的伸缩尺寸。
TranslateAnimation
1 | Animation translateAnimation = new TranslateAnimation(0.1f, 100.0f, 0.1f, 100.0f); |
TranslateAnimation构造函数中:
第一个参数fromXDelta,第二个参数toXDelta:分别是动画起始、结束时X坐标;
第三个参数fromYDelta,第四个参数toYDelta:分别是动画起始、结束时Y坐标。
XML形式
XML文件可以用来定义动画,文件放于 res/anim/ 目录下。
根元素包括:
- <alpha>
- <scale>
- <translate>
- <rotate>
- <set>
很明显,前四个对应了四种补间动画。
而最后一个 <set> 则是动画集,它可以包含多个其他标签,也可以嵌套 <set> 标签。
使用 <set> 的默认情况下,所有动画会同时播放。
如果想按顺序播放,则需要制定 startOffset 属性。
此外,还可以通过设置 interpolator 控制动画变化的速率,比如匀速、加减速等等。
<alpha>
<alpha> 可以实现透明度渐变的动画效果,也就是淡入淡出的效果,可通过设置下面三个属性来设置淡入或淡出效果:
- android:duration 动画从开始到结束持续的时长,单位为毫秒
- android:fromAlpha 动画开始时的透明度,0.0为全透明,1.0为不透明,默认为1.0
- android:toAlpha 动画结束时的透明度,0.0为全透明,1.0为不透明,默认为1.0
当设置开始时透明度为0.0,结束时为1.0,就能实现淡入效果;
相反,当设置开始时透明度为1.0,结束时为0.0,那就能实现淡出效果。
示例如下:1
2
3
4
5
6<!-- res/anim/fade_in.xml -->
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
将这动画效果添加到View上也只需要一行代码:1
view.startAnimation(AnimationUtils.loadAnimation(this, R.anim.fade_in));
如果需要重用这个动画,也可以将其抽离出来,对应的类是AlphaAnimation类:1
2AlphaAnimation fadeInAnimation = (AlphaAnimation) AnimationUtils.loadAnimation(this, R.anim.fade_in);
view.startAnimation(fadeInAnimation);
<scale>
<scale> 可以实现缩放的动画效果,主要的属性如下:
- android:fromXScale 动画开始时X坐标上的缩放尺寸
- android:toXScale 动画结束时X坐标上的缩放尺寸
- android:fromYScale 动画开始时Y坐标上的缩放尺寸
- android:toYScale 动画结束时Y坐标上的缩放尺寸
(以上四个属性,0.0表示缩放到没有,1.0表示正常无缩放,小于1.0表示收缩,大于1.0表示放大) - android:duration 动画从开始到结束持续的时长,单位为毫秒
- android:pivotX 缩放时的固定不变的X坐标,一般用百分比表示,0%表示左边缘,100%表示右边缘
- android:pivotY 缩放时的固定不变的Y坐标,一般用百分比表示,0%表示顶部边缘,100%表示底部边缘
示例如下:1
2
3
4
5
6
7
8
9
10<!-- res/anim/zoom_out.xml -->
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="0%"
android:pivotY="100%"
android:toXScale="1.5"
android:toYScale="1.5" />
<scale> 标签对应的类为ScaleAnimation,添加到View上的用法和AlphaAnimation一样,代码如下:1
2ScaleAnimation zoomOutAnimation = (ScaleAnimation) AnimationUtils.loadAnimation(this, R.anim.zoom_out);
view.startAnimation(zoomOutAnimation);
<translate>
<translate> 可以实现位置移动的动画效果,可以是垂直方向的移动,也可以是水平方向的移动。
坐标的值可以有三种格式:
- 从-100到100,以”%”结束,表示相对于View本身的百分比位置;
- 如果以”%p”结束,表示相对于View的父View的百分比位置;
- 如果没有任何后缀,表示相对于View本身具体的像素值。
主要的属性如下:
- android:duration 动画从开始到结束持续的时长,单位为毫秒
- android:fromXDelta 起始位置的X坐标的偏移量
- android:toXDelta 结束位置的X坐标的偏移量
- android:fromYDelta 起始位置的Y坐标的偏移量
- android:toYDelta 结束位置的Y坐标的偏移量
以下代码实现的是从左到右的移动效果,起始位置为相对于控件本身-100%的位置,即在控件左边,与控件本身宽度一致的位置;结束位置为相对于父控件100%的位置,即会移出父控件右边缘的位置:1
2
3
4
5
6
7
8<!-- res/anim/move_left_to_right.xml -->
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromXDelta="-100%"
android:fromYDelta="0"
android:toXDelta="100%p"
android:toYDelta="0" />
<translate> 标签对应的类为TranslateAnimation:1
2TranslateAnimation moveAnimation = (TranslateAnimation) AnimationUtils.loadAnimation(this, R.anim.move_left_to_right);
view.startAnimation(moveAnimation);
<rotate>
<rotate> 可以实现旋转的动画效果,主要的属性如下:
- android:duration 动画从开始到结束持续的时长,单位为毫秒
- android:fromDegrees 旋转开始的角度
- android:toDegrees 旋转结束的角度
- android:pivotX 旋转中心点的X坐标,纯数字表示相对于View本身左边缘的像素偏移量;带”%”后缀时表示相对于View本身左边缘的百分比偏移量;带”%p”后缀时表示相对于父View左边缘的百分比偏移量
- android:pivotY 旋转中心点的Y坐标,纯数字表示相对于View本身顶部边缘的像素偏移量;带”%”后缀时表示相对于View本身顶部边缘的百分比偏移量;带”%p”后缀时表示相对于父View顶部边缘的百分比偏移量
以下示例代码旋转角度从0到360,即旋转了一圈,旋转的中心点都设为了50%,即是View本身中点的位置:1
2
3
4
5
6
7
8<!-- res/anim/rotate_one.xml -->
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%" />
<rotate> 标签对应的类为RotateAnimation:1
2RotateAnimation rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(this, R.anim.rotate_one);
view.startAnimation(rotateAnimation);
<set>
<set> 标签可以将多个动画组合起来,变成一个动画集。
比如想将一张图片缩放的同时也做移动,这时候就要用<set>标签组合缩放动画和移动动画了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<!-- res/anim/move_and_scale.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000">
<translate
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="200%"
android:toYDelta="0" />
<scale
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="0%"
android:pivotY="100%"
android:toXScale="1.5"
android:toYScale="1.5" />
</set>
以上代码实现的动画效果为向右移动的同时也同步放大。
<set> 标签在视图动画中除了可以组合<alpha>, <scale>, <translate>, <rotate>这四种标签,也可以嵌套其他<set>标签。
另外,<set> 标签可嵌套的标签元素并不只有这几个,后面谈到属性动画时会再讲其他的标签及用法。
通用属性
除了android:duration,补间动画还有很多通用属性:
- android:duration 动画从开始到结束持续的时长,单位为毫秒
- android:detachWallpaper 设置是否在壁纸上运行,只对设置了壁纸背景的窗口动画(window animation)有效。设为true,则动画只在窗口运行,壁纸背景保持不变
- android:fillAfter 设置为true时,动画执行完后,View会停留在动画的最后一帧;默认为false;如果是动画集,需在
标签中设置该属性才有效 - android:fillBefore 设置为true时,动画执行完后,View回到动画执行前的状态,默认即为true
- android:fillEnabled 设置为true时,android:fillBefore的值才有效,否则android:fillBefore会被忽略
- android:repeatCount 设置动画重复执行的次数,默认为0,即不重复;可设为-1或infinite,表示无限重复
- android:repeatMode 设置动画重复执行的模式,可设为以下两个值其中之一:
- restart 动画重复执行时从起点开始,默认为该值
- reverse 动画会反方向执行
- android:startOffset 设置动画执行之前的等待时长,毫秒为单位;重复执行时,每次执行前同样也会等待一段时间
- android:zAdjustment 表示被设置动画的内容在动画运行时在Z轴上的位置,取值为以下三个值之一:
- normal 默认值,保持内容在Z轴上的位置不变
- top 保持在Z周最上层
- bottom 保持在Z轴最下层
- android:interpolator 设置动画速率的变化,比如加速、减速、匀速等,需要指定Interpolator资源
- <set>标签还有个android:shareInterpolator属性,设置为true时则可将interpolator应用到所有子元素中
Interpolator
通过interpolator可以定义动画速率变化的方式,比如加速、减速、匀速等。
下面列出了Android本身提供的各类Interpolator。
- AccelerateDecelerateInterpolator
@android:anim/accelerate_decelerate_interpolator
在动画开始与结束时速率改变比较慢,在中间的时候加速 - AccelerateInterpolator
@android:anim/accelerate_interpolator
在动画开始时速率改变比较慢,然后开始加速 - AnticipateInterpolator
@android:anim/anticipate_interpolator
动画开始的时候向后然后往前抛 - AnticipateOvershootInterpolator
@android:anim/anticipate_overshoot_interpolator
动画开始的时候向后然后向前抛,会抛超过目标值后再返回到最后的值 - BounceInterpolator
@android:anim/bounce_interpolator
动画结束的时候会弹跳 - CycleInterpolator
@android:anim/bounce_interpolator
动画循环做周期运动,速率改变沿着正弦曲线 - DecelerateInterpolator
@android:anim/decelerate_interpolator
在动画开始时速率改变比较快,然后开始减速 - LinearInterpolator
@android:anim/decelerate_interpolator
动画匀速播放 - OvershootInterpolator
@android:anim/overshoot_interpolator
动画向前抛,会抛超过最后值,然后再返回
自定义Interpolator
如果系统提供的以上Interpolator还不符合你的效果,也可以自定义。
自定义的方式有两种:
- 一种是通过继承 Interpolator 父类或其子类;
- 另一种是通过自定义的xml文件,可以更改上表中Interpolator的属性
自定义的xml文件需存放于res/anim/目录下,根标签与上面列出的Interpolator相对应。
注意,<accelerateDecelerateInterpolator>、<bounceInterpolator>和<linearInterpolator>没有可变的属性。
- <accelerateInterpolator>
- android:factor 浮点值,加速的速率,默认为1
- <anticipateInterpolator>
- android:tension 浮点值,向后的拉力,默认为2,当设为0时,则不会有向后的动画了
- <anticipateOvershootInterpolator>
- android:tension 浮点值,向后的拉力,默认为2,当设为0时,则不会有向后的动画了
- android:extraTension 浮点值,拉力的倍数,默认为1.5(2*1.5),当设为0时,则不会有拉力了
- <cycleInterpolator>
- android:cycles 整数值,循环的次数,默认为1
- <decelerateInterpolator>
- android:factor 浮点值,减速的速率,默认为1
- <overshootInterpolator>
- android:tension 浮点值,超出终点后的拉力,默认为2
举例如下:1
2
3
4
5
6<!-- res/anim/my_interpolator.xml -->
<?xml version="1.0" encoding="utf-8"?>
<anticipateOvershootInterpolator
xmlns:android="http://schemas.android.com/apk/res/android"
android:tension="3"
android:extraTension="2" />
接着,将其设置到要应用的动画的android:interpolator属性即可,代码如下:1
2
3
4
5
6
7
8
9<!-- res/anim/rotate_one.xml -->
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:interpolator="@anim/my_interpolator" />
帧动画
帧动画的原理是在“连续的关键帧”中分解动画动作,也就是在时间轴的每帧上逐帧绘制不同的内容,使其连续播放而成动画。
在Android中逐帧动画需要得到AnimationDrawable类的支持。
它主要用来创建一个逐帧动画,并且可以对帧进行拉伸,把它设置为View的背景即可使用AnimationDrawable.start()方法播放。
同样,创建帧动画的形式有XML定义的资源文件和Java代码创建两种。
AnimationDrawable的主要方法包括:1
2
3
4
5
6
7
8
9void start():开始播放逐帧动画。
void stop():停止播放逐帧动画。
void addFrame(Drawable frame,int duration):为AnimationDrawable添加一帧,并设置持续时间。
int getDuration(int i):得到指定index的帧的持续时间。
Drawable getFrame(int index):得到指定index的帧Drawable。
int getNumberOfFrames():得到当前AnimationDrawable的所有帧数量。
boolean isOneShot():当前AnimationDrawable是否执行一次,返回true执行一次,false循环播放。
boolean isRunning():当前AnimationDrawable是否正在播放。
void setOneShot(boolean oneShot):设置AnimationDrawable是否执行一次,true执行一次,false循环播放
XML形式
1 | <?xml version="1.0" encoding="utf-8"?> |
再在java代码中获得该动画:1
2
3
4
5
6// 通过逐帧动画的资源文件获得AnimationDrawable示例
AnimationDrawable frameAnim = (AnimationDrawable) getResources().getDrawable(R.drawable.bullet_anim);
// 把AnimationDrawable设置为ImageView的背景
imageView.setBackgroundDrawable(frameAnim);
// 开始动画
frameAnim.start();
或者也可以:1
2
3
4
5
6
7// 将动画设为imageView的背景
imageView.setBackgroundResource(R.drawable.drawable_anim);
// 获取AnimationDrawable
anim = (AnimationDrawable) imageView.getBackground();
anim.stop();
// 开始动画
anim.start();
在Android中,除了可以通过XML文件定义一个逐帧动画之外,还可以通过AnimationDrawable.addFrame()方法为AnimationDrawable添加动画帧。
注意点
使用帧动画有这么几个注意点:
- 要在代码中调用ImageView的setBackgroundDrawable,不能直接在XML布局文件中设置其src属性
- 在动画start()之前要先stop(),不然在第一次动画之后会停在最后一帧,这样动画就只会触发一次
- 不要在onCreate中调用start,因为AnimationDrawable还没有完全跟Window相关联,如果想要界面显示时就开始动画的话,可以在onWindowFoucsChanged()中调用start()。