Android 动画主要可以分为两种,一种就是视图动画,一种就是属性动画,这里我们先了解一下视图动画。
而视图动画也分为 Tween(补间)动画和 Frame(帧)动画。
- 补间动画
补间动画就是一个 View 从开始状态到结束状态变化的过程,补间动画有四种基本变化:渐变、平移、缩放、旋转,这四种基本动画效果都可以通过 xml 和代码两种方式来实现。补间动画如果要使用 xml 方法,就需要在 res 文件目录下创建 anim文件夹,将动画的 xml 文件放在其中:
xml 主要共有熟悉:
属性名 | 说明 |
android:duration="" | 动画持续时间,单位:毫秒 |
android:startOffset="" | 动画开始时间,单位:毫秒 |
- 渐变动画
- xml 方法
xml 方法主要属性:
属性名 | 说明 |
android:fromAlpha="" | 动画开始时透明度,0.0 表示全透明,1.0 表示不透明 |
android:toAlpha="" | 动画结束时透明度,0.0 表示全透明,1.0 表示不透明 |
代码:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="1000"/>
</set>
// 通过 xml 完成渐变动画
private void doAlphaByXML(){
Animation animation = AnimationUtils.loadAnimation(this,R.anim.anim_tween_alpha);
image.startAnimation(animation);
}
- 代码方法
// 通过代码完成渐变动画
private void doAlphaByCode(){
AlphaAnimation animation = new AlphaAnimation(1.0f,0.0f);
animation.setDuration(1000);
image.startAnimation(animation);
}
- 平移动画
- xml 方法
属性名 | 说明 |
android:fromXDelta="" | X 轴开始地方,0表示自身 X 坐标,单位:像素 |
android:fromYDelta="" | Y 轴开始地方,0表示自身 Y 坐标,单位:像素 |
android:toXDelta="" | X 轴结束地方,0表示自身 X 坐标,单位:像素 |
android:toYDelta="" | Y 轴结束地方,0表示自身 Y 坐标,单位:像素 |
代码:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="5"
android:toXDelta="100"
android:fromYDelta="0"
android:toYDelta="100"
android:duration="1000"
/>
</set>
// 通过 xml 完成平移动画
private void doTranslateByXML(){
Animation animation = AnimationUtils.loadAnimation(this,R.anim.anim_tween_translate);
image.startAnimation(animation);
}
- 代码方法
// 通过代码完成平移动画
private void doTranslateByCode(){
TranslateAnimation animation = new TranslateAnimation(0,100,0,100);
animation.setDuration(1000);
image.startAnimation(animation);
}
前提说明,因为缩放动画和旋转动画的 xml 属性,以及代码方法的参数有共同之处,这里先做一个说明。
属性名 | 说明 |
android:pivotX="" | 缩放或者旋转时的中心点的 X 坐标 |
android:pivotY="" | 缩放或者旋转时的中心点的 Y 坐标 |
- 缩放动画
- xml 方法
属性名 | 说明 |
android:fromXScale="" | X 轴开始的大小,1 表示自身大小,单位:比例 |
android:toXScale="" | X 轴结束的大小,1 表示自身大小,单位:比例 |
android:fromYScale="" | Y 轴开始的大小,1 表示自身大小,单位:比例 |
android:toYScale="" | Y 轴结束的大小,1 表示自身大小,单位:比例 |
代码:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:fromXScale="0.5"
android:toXScale="2"
android:fromYScale="0.5"
android:toYScale="2"
android:pivotX="0"
android:pivotY="0"
android:duration="1000"/>
</set>
// 通过 xml 完成缩放动画
private void doScaleByXML(){
Animation animation = AnimationUtils.loadAnimation(this,R.anim.anim_tween_scale);
image.startAnimation(animation);
}
- 代码方式
// 通过代码完成缩放动画
private void doScaleByCode(){
ScaleAnimation animation = new ScaleAnimation(0.5f,2,0.5f,2,0,0);
animation.setDuration(1000);
image.startAnimation(animation);
}
- 旋转动画
- xml 方法
xml 方法主要属性:
属性名 | 说明 |
android:fromDegrees="" | 动画开始的角度,单位:度(正从顺时针) |
android:toDegrees="" | 动画结束的角度,单位:度(正从顺时针) |
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:fromDegrees="0"
android:toDegrees="360"
android:duration="1000"
android:pivotX="100%"
android:pivotY="100%"/>
</set>
// 通过 xml 完成旋转动画
private void doRotateByXML(){
Animation animation = AnimationUtils.loadAnimation(this,R.anim.anim_tween_rotate);
image.startAnimation(animation);
}
注意:android:pivotX="" 和 android:pivotY="" 有三种方式,如下(以 android:pivotX="" 举例),三种方式的坐标原点都是以自身 View 的左上角。
方式 | 结果 |
android:pivotX="100"(float 方式) | 以像素为大小 |
android:povotX="50%"(百分比方式) | 以自身为大小,指占比自己大小多少 |
android:prvotX="50%p"(百分比 p 方式) | 以页面为大小,指占比页面多少 |
这个,可能文字描述起来比较抽象,大家写一下马上就能明白。
- 代码方法
// 通过代码完成旋转动画
private void doRotateByCode(){
RotateAnimation animation = new RotateAnimation(0,360,100,100);
animation.setDuration(1000);
image.startAnimation(animation);
}
注意:旋转和缩放动画使用代码方式时,其不止一个构造方法,文中,写的构造方法是没有 pivotXType 和 pivotYType 的,没有选 Type,默认为像素。
- 组合动画
当然,我们使用动画时,不可能只使用一种动画,我们只有组合使用,才能达到一些想要的动画效果:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:toXDelta="100"
android:fromYDelta="0"
android:toYDelta="100"
android:duration="1000"/>
<rotate
android:pivotX="-100"
android:pivotY="-100"
android:fromDegrees="0"
android:toDegrees="360"
android:duration="2000"
android:startOffset="1000"/>
<rotate
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="0"
android:toDegrees="720"
android:duration="2000"
android:startOffset="1000"/>
<scale
android:fromXScale="1"
android:toXScale="3"
android:fromYScale="1"
android:toYScale="3"
android:pivotX="0"
android:pivotY="0"
android:duration="2000"
android:startOffset="3000"/>
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="2000"
android:startOffset="3000"/>
</set>
是时候放一波效果图了:
大家可以看到,视图动画结束后会默认回到原来的位置,我们可以使用该函数,让其动画结束后留在原地:
animation.setFillAfter(true);
注意:视图动画有一个致命缺点,就是视图动画动的只是它的外表,它的坐标并没有随着外表动而动,这是什么意思呢?就是当一个视图动画结束后并且留在动画结束后的位置时,其是不能响应点击事件的,而点击动画原来的位置,却发现其响应了点击事件,大家可以试试。
那这个问题怎么解决呢?比较暴力的方法,就是写两个 View,新的 View 放在动画结束的位置,旧的 View 在动画结束后消失,并且让其不可被点击,显示新的 View,然后响应新的 View 的点击事件。
还有什么办法呢?就是使用 Android 3.0 后的属性动画,属性动画移动的不仅是外表,还有其属性,这个问题就可以完美解决。
当然,动画也是有监听器的:
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
// 动画开始时
}
@Override
public void onAnimationEnd(Animation animation) {
// 动画结束时
}
@Override
public void onAnimationRepeat(Animation animation) {
// 动画重复时
}
});
- 帧动画
帧动画又是什么意思呢?大家都知道电影吧,电影是可以取帧的,每一帧就是一张图片,将很多帧组合在一起就成了动画,帧动画就好比将很多张图片连续展示,组成一幅动的画:
帧动画,现在已经很少使用了,自从 Android 3.0 的属性动画出来以后补间动画都用的少了,这里也来说一说实现补间动画的方式吧。
- xml 方法
属性名 | 说明 |
android:drawable="" | 当前帧的图片 |
android:duration="" | 当前帧的播放时间,单位:毫秒 |
通过 xml 实现帧动画和补间动画是不太一样的,帧动画的 xml 文件是在 drawable 目录下的,并且以 <animation-list> 为根节点。
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/frame1" android:duration="50"/>
<item android:drawable="@drawable/frame2" android:duration="50"/>
<item android:drawable="@drawable/frame3" android:duration="50"/>
<item android:drawable="@drawable/frame4" android:duration="50"/>
<item android:drawable="@drawable/frame5" android:duration="50"/>
<item android:drawable="@drawable/frame6" android:duration="50"/>
<item android:drawable="@drawable/frame7" android:duration="50"/>
<item android:drawable="@drawable/frame8" android:duration="50"/>
<item android:drawable="@drawable/frame9" android:duration="50"/>
<item android:drawable="@drawable/frame10" android:duration="50"/>
<item android:drawable="@drawable/frame11" android:duration="50"/>
<item android:drawable="@drawable/frame12" android:duration="50"/>
<item android:drawable="@drawable/frame13" android:duration="50"/>
<item android:drawable="@drawable/frame14" android:duration="50"/>
<item android:drawable="@drawable/frame15" android:duration="50"/>
<item android:drawable="@drawable/frame16" android:duration="50"/>
<item android:drawable="@drawable/frame17" android:duration="50"/>
<item android:drawable="@drawable/frame18" android:duration="50"/>
<item android:drawable="@drawable/frame19" android:duration="50"/>
<item android:drawable="@drawable/frame20" android:duration="50"/>
<item android:drawable="@drawable/frame21" android:duration="50"/>
<item android:drawable="@drawable/frame22" android:duration="50"/>
<item android:drawable="@drawable/frame23" android:duration="50"/>
<item android:drawable="@drawable/frame24" android:duration="50"/>
<item android:drawable="@drawable/frame25" android:duration="50"/>
</animation-list>
// 通过 xml 实现帧动画
private void doFrameByXML(){
imageView.setBackgroundResource(R.drawable.anim_frame);
AnimationDrawable drawable = (AnimationDrawable) imageView.getBackground();
if (!drawable.isRunning()){
drawable.start();
}
}
代码方法:
// 通过代码实现帧动画
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
private void doFrameByCode(){
AnimationDrawable drawable = new AnimationDrawable();
drawable.addFrame(getResources().getDrawable(R.drawable.frame1),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame2),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame3),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame4),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame5),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame6),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame7),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame8),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame9),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame10),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame11),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame12),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame13),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame14),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame15),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame16),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame17),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame18),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame19),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame20),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame21),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame22),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame23),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame24),50);
drawable.addFrame(getResources().getDrawable(R.drawable.frame25),50);
imageView.setBackground(drawable);
// 是否只播放一次
drawable.setOneShot(false);
if (!drawable.isRunning()){
drawable.start();
}
}
效果图:
大家需要注意的是,如果是自己测试做帧动画,一定要注意图片不能太大,不能过多,要不然会造成突然加载大量图片导致 OOM。
项目地址:源代码