引言
Android动画分为两类, 分别是视图动画(View Animation)和属性动画(Property Animation)。其中View动画又分为帧动画(Frame Animation)和补间动画(Tween Animation),帧动画可以去加载一系列可绘制的Drawable逐帧实现动画,补间动画可以执行一系列简单的变化,这些变化包括旋转、位移、缩放等等。
1 帧动画(Frame Animation)
帧动画顾名思义就是由图片按照顺序一帧一帧组合而成的动画。如下图1-1所示,图片按照run_1到run_16播放就可以形成一个跑步的动画。
Android实现帧动画有两种方式,一种是xml文件实现,这种方式是帧动画最常用的实现方式,另一种是JAVA代码方式实现。它们的本质都是通过使用AnimationDrawable按顺序添加一系列Drawable来创建动画,并把动画赋值给View对象。
1.1 xml实现帧动画
在Drawable下新建文件,然后使用animation-list并嵌套一系列的item,每一个item代表着一帧。
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false" >
<!--android:oneshot="false" ,true表示只播放一次,false则相反-->
<item android:drawable="@drawable/run_1" android:duration="50"></item>
<item android:drawable="@drawable/run_2" android:duration="50"></item>
<item android:drawable="@drawable/run_3" android:duration="50"></item>
<item android:drawable="@drawable/run_4" android:duration="50"></item>
<item android:drawable="@drawable/run_5" android:duration="50"></item>
<item android:drawable="@drawable/run_6" android:duration="50"></item>
<item android:drawable="@drawable/run_7" android:duration="50"></item>
<item android:drawable="@drawable/run_8" android:duration="50"></item>
<item android:drawable="@drawable/run_9" android:duration="50"></item>
<item android:drawable="@drawable/run_10" android:duration="50"></item>
<item android:drawable="@drawable/run_11" android:duration="50"></item>
<item android:drawable="@drawable/run_12" android:duration="50"></item>
<item android:drawable="@drawable/run_13" android:duration="50"></item>
<item android:drawable="@drawable/run_14" android:duration="50"></item>
<item android:drawable="@drawable/run_15" android:duration="50"></item>
<item android:drawable="@drawable/run_16" android:duration="50"></item>
</animation-list>
下面是核心代码,首先获取AnimationDrawable,然后赋值给iamgeview做背景,最后调用AnimationDrawable的start、stop方法,就可以像图1-2一样开始动画或者停止动画。
//获取动画
animationDrawable = (AnimationDrawable) getResources().getDrawable(R.drawable.run_anim);
img.setImageDrawable(animationDrawable);
//开始播放动画
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (animationDrawable !=null && !animationDrawable.isRunning()) {
animationDrawable.start();
}
}
});
//停止播放动画
stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (animationDrawable !=null &&animationDrawable.isRunning()) {
animationDrawable.stop();
}
}
});
1.2 JAVA代码实现帧动画
首先实例化AnimationDrawable,然后再获取Drawable下的资源文件,按照顺序逐帧添加到AnimationDrawable中,最后将AnimationDrawable赋值给View对象使用。
private void createAnimation() {
animationDrawable =new AnimationDrawable();
int id;
for (int i =1; i <=16; i++) {
id = getResources().getIdentifier("run_" + i,"drawable", getPackageName());
//根据id获取到Drawable
Drawable drawable = getResources().getDrawable(id);
animationDrawable.addFrame(drawable,50);
}
//是否只运行一次
animationDrawable.setOneShot(false);
//给imageView设置动画
img.setImageDrawable(animationDrawable);
}
2 补间动画(Tween Animation)
补间动画是通过执行一系列变化来达到动画效果的,这一系列的变化包括旋转(rotate)、位移(translate)、缩放(scale)和透明度(alpha),对应的类包括 RotateAnimation,TranslateAnimation,ScaleAnimation,AlphaAnimation,AnimationSet五个类,它们都继承自Animation抽象类。补间动画的实现方式也是两种,第一种是在xml文件中实现,另一种是JAVA代码实现。
因为对应的五个类继承自Animation,所以它们也继承了Animation的属性,除此之外它们还有自己特有的属性。下列是继承自Animation的属性:
android:duration="long" //定义动画运行时间,单位:毫秒。
android:fillAfter="boolean" //定义动画结束后应用动画转变(是否停留在最后一帧)。默认值是false。
android:fillBefore="boolean" //定义动画开始之前应用动画转变(是否直接从动画转变的第一帧开始)。默认值为true。
android:fillEnaabled="boolean" //如果为true则应用fillBefore的值,否则忽略fillBefore的值。默认值为false。
android:interpolator="@[+][package:]type/name" | "?[package:]type/name" //定义动画过渡用的插值器,例如:android:interpolator="@android:anim/linear_interpolator"。
android:repeatCount="int" //定义动画重复多少次。默认值为0。
android:repeatMode=["restart " | "reverse"] //定义repeatCount大于0时动画结束时的重复模式,restart 是从开始的地方重新播 放,reverse是从结束的地方重新播放。默认值是restart。
android:startOffset="int" //定义动画开始前延迟,单位:毫秒。
android:zAdjustment=["bottom" | "normal" | " top"] //允许调整动画在运行期间的z轴顺序。默认值为normal。
android:detackWallpaper="boolean" //窗口动画特殊属性,如果这个窗口在壁纸之上,请不要使用它为墙纸设置动画。
2.1 旋转(rotate)
RotateAnimation是控制对象旋转的动画,这个旋转发生在X-Y平面上。你可以指定旋转的中心点,其中(0,0)是左上角的点,如果你不指定中心点,那么(0,0)就是默认的旋转中心点。
旋转特有的属性:
android:fromDegrees="float" //定义动画开始前的角度。
android:toDegrees="float" //定义动画结束时的角度。
android:pivotX="float" //定义旋转点的X轴坐标,0是左上角。这个值可以是百分比(旋转坐标在自身x轴的百分比 )或者如果pivotXType 是ABSOLUTE这个值可以是一个绝对数字。
android:pivotY="float" //定义旋转点的Y轴坐标,0是左上角。这个值可以是百分比( 旋转坐标在自身y轴的百分比 )或者如果 pivotXType 是 ABSOLUTE这个值可以是一个绝对数字。
下面两个属性pivotXType和pivotYType不能在XML中使用,例如使用了pivotXType会报错误“ error: attribute android:pivotXType not found. ”
pivotXType //指定应该如何去解释pivotXValue ,可以使用其中一个Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF或 Animation.RELATIVE_TO_PARENT。
pivotYType //指定应该如何去解释 pivotYValue ,可以使用其中一个Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF或 Animation.RELATIVE_TO_PARENT。
Animation.ABSOLUTE //指定尺寸(dimension)是一个绝对像素数量。使用之后pivotX 或pivotY 的值都是绝对像素数量。
Animation.RELATIVE_TO_SELF // 指定尺寸(dimension)是一个浮点数,这个浮点数用来乘动画对象的宽或高。例如使用之后动画最终的x轴 =(pivotX 的值) X (动画对象的宽 )。
Animation.RELATIVE_TO_PARENT // 指定尺寸(dimension)是一个浮点数,这个浮点数用来乘动画父对象的宽或高。例如使用之后动画最终的x轴 =(pivotX 的值) X ( 动画父对象的宽 )。
2.1.1 在xml中实现旋转
先看旋转效果图,如下图2-1:
首先要做的就是在res下新建anim目录,然后在anim目录下创建m_rotate.xml,以rotate作为根元素进行编写,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromDegrees="0"
android:interpolator="@android:anim/accelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="720" >
</rotate>
编写完xml文件后,在java代码中使用AnimationUtils里的loadAnimation方法加载动画,并使用Animation。
animation = AnimationUtils.loadAnimation(this, R.anim.m_rotate);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (animation !=null) {
img.startAnimation(animation);
}
}
});
2.1.2 纯代码实现旋转
纯代码实现旋转需要使用到RotateAnimation类。RotateAnimation有以下四个构造方法,一般①不常用,使用②③④居多。
①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)
因为前面没有pivotXType和pivotYType的实例,因此使用④构造RotateAnimation,先看效果图2-2 。
构造方法很简单,pivotXType使用Animation.RELATIVE_TO_SELF,pivotXValue使用1f,因此动画对象的x轴旋转点从0向右边位移到自身1倍的位置上,而pivotXType也使用Animation.RELATIVE_TO_SELF,但是pivotXValue使用0f,因此Y轴旋转点没变。
animation =new RotateAnimation(0,720, Animation.RELATIVE_TO_SELF,1f, Animation.RELATIVE_TO_SELF,0f);
animation.setDuration(1000);
animation.setInterpolator(this, android.R.anim.accelerate_interpolator);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (animation !=null) {
img.startAnimation(animation);
}
}
});
2.2 平移(translate)
TranslateAnimation是控制对象位置的动画。
旋转特有的属性:
android:fromXDelta="float" //在动画改变之前的X轴坐标
android:fromYDelta="float" //在动画改变之前的X轴坐标
android:toXDelta="float" //动画改变之后的X轴坐标
android:toYDelta="float" //动画改编之后的X轴坐标
下面四个属性 fromXType、 fromYType、toXType、toYType不能在XML中使用,例如使用了fromXType 会报错误“ error: attribute android:pivotXType not found. ”
fromXType//指定应该如何去解释 fromXType,可以使用其中一个Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF或 Animation.RELATIVE_TO_PARENT。
fromYType//指定应该如何去解释 fromYType,可以使用其中一个Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF或 Animation.RELATIVE_TO_PARENT。
toXType//指定应该如何去解释 toXType,可以使用其中一个Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF或 Animation.RELATIVE_TO_PARENT。
toYType//指定应该如何去解释 toYType,可以使用其中一个Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF或 Animation.RELATIVE_TO_PARENT。
Animation.ABSOLUTE //指定尺寸(dimension)是一个绝对像素数量。使用之后 pivotX 或 pivotY 的值都是绝对像素数量。
Animation.RELATIVE_TO_SELF // 指定尺寸(dimension)是一个浮点数,这个浮点数用来乘动画对象的宽或高。例如使用之后动画最终的x轴 =(pivotX 的值) X ( 动画对象的宽 )。
Animation.RELATIVE_TO_PARENT // 指定尺寸(dimension)是一个浮点数,这个浮点数用来乘动画父对象的宽或高。例如使用之后动画最终的x轴 =(pivotX 的值) X ( 动画父对象的宽 )。
2.2.1 在xml实现平移
先看平移效果图,如下图2-3:
首先要做的就是在res下新建anim目录,然后在anim目录下创建m_translate.xml,以translate作为根元素进行编写,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="50%"
android:toYDelta="50%">
</translate>
编写完xml文件后,在java代码中使用AnimationUtils里的loadAnimation方法加载动画,并使用Animation。
animation = AnimationUtils.loadAnimation(this, R.anim.m_translate);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (animation !=null) {
img.startAnimation(animation);
}
}
});
2.2.2 纯代码实现位移
纯代码实现旋转需要使用到TranslateAnimation类。TranslateAnimation有以下三个构造方法,一般①不常用,使用②③居多。
①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)
因为前面没有fromXType、 fromYType、toXType、toYType的实例,因此使用③构造RotateAnimation,先看效果图2-4 。
构造方法很简单,fromXType使用Animation.RELATIVE_TO_SELF,fromXValue使用1f,因此动画对象x轴的平移距离为自身宽的一倍,fromYType使用Animation.RELATIVE_TO_SELF,toYType使用1f,因此动画对象Y轴的平移距离为自身高的一倍。
animation =new TranslateAnimation(Animation.RELATIVE_TO_SELF,0f, Animation.RELATIVE_TO_SELF,1f,Animation.RELATIVE_TO_SELF,0f, Animation.RELATIVE_TO_SELF,1f);
animation.setDuration(2000);
animation.setInterpolator(this, android.R.anim.linear_interpolator);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (animation !=null) {
img.startAnimation(animation);
}
}
});
2.3 缩放(scale)
ScaleAnimation是控制对象缩放的动画,你可以指定缩放的中心点。
缩放特有的属性:
android:fromXScale="float" //动画开始前横轴缩放因子
android:fromYScale="float" //动画开始前纵轴缩放因子
android:toXScale="float" //动画结束时横轴缩放因子
android:toYScale="float" //动画结束时纵轴缩放因子
android:pivotX="float" //定义缩放点的X轴坐标,0是左上角。这个值可以是百分比(1.0是100% )或者如果pivotXType 是ABSOLUTE这个值可以是一个绝对数字。
android:pivotY="float" //定义旋转点的Y轴坐标,0是左上角。这个值可以是百分比( 1.0是100% )或者如果 pivotXType 是 ABSOLUTE这个值可以是一个绝对数字。
下面两个属性pivotXType和pivotYType不能在XML中使用,例如使用了pivotXType会报错误“ error: attribute android:pivotXType not found. ”
pivotXType //指定应该如何去解释pivotXValue ,可以使用其中一个Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF或 Animation.RELATIVE_TO_PARENT。
pivotYType //指定应该如何去解释 pivotYValue ,可以使用其中一个Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF或 Animation.RELATIVE_TO_PARENT。
Animation.ABSOLUTE //指定尺寸(dimension)是一个绝对像素数量。使用之后pivotX 或pivotY 的值都是绝对像素数量。
Animation.RELATIVE_TO_SELF // 指定尺寸(dimension)是一个浮点数,这个浮点数用来乘动画对象的宽或高。例如使用之后动画最终的x轴 =(pivotX 的值) X (动画对象的宽 )。
Animation.RELATIVE_TO_PARENT // 指定尺寸(dimension)是一个浮点数,这个浮点数用来乘动画父对象的宽或高。例如使用之后动画最终的x轴 =(pivotX 的值) X ( 动画父对象的宽 )。
2.3.1 在xml实现缩放
先看看缩放效果图2-5:
首先要做的就是在res下新建anim目录,然后在anim目录下创建m_scale.xml,以scale作为根元素进行编写,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXScale="1"
android:fromYScale="1"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="2"
android:toYScale="2">
</scale>
编写完xml文件后,在java代码中使用AnimationUtils里的loadAnimation方法加载动画,并使用Animation。
animation = AnimationUtils.loadAnimation(this, R.anim.m_scale);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (animation !=null) {
img.startAnimation(animation);
}
}
});
2.3.2 纯代码实现缩放
纯代码实现旋转需要使用到ScaleAnimation类。ScaleAnimation有以下四个构造方法,一般①不常用,使用②③④居多。
①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)
因为前面没有pivotXType和pivotYType的实例,因此使用④构造RotateAnimation,先看效果图2-6 。
构造方法很简单,fromXScale使用了1f,toXScale使用了2f,所以x轴放大了1倍。fromYScale使用了1f,toYScale使用了1f,所以Y轴没变。pivotXType使用Animation.RELATIVE_TO_SELF,pivotXValue使用0.5f,因此动画对象的x轴缩放点从0向右边位移到自身0.5倍的位置上,而pivotXType也使用Animation.RELATIVE_TO_SELF,但是pivotXValue使用0.5f,因此动画对象的x轴缩放点从0向下边位移到自身0.5倍的位置上。
animation =new ScaleAnimation(1f,2f,1f,1f, Animation.RELATIVE_TO_SELF,0.5f, Animation.RELATIVE_TO_SELF,0.5f);
animation.setDuration(2000);
animation.setInterpolator(this, android.R.anim.linear_interpolator);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (animation !=null) {
img.startAnimation(animation);
}
}
});
2.4 透明度(alpha)
AlphaAnimation是控制对象alpha等级的动画,用于淡入淡出效果。
透明度特有的属性:
android:fromAlpha="float" //开始动画alpha值,其中1.0表示完全不透明,0.0表示完全透明
android:toAlpha="float" //结束动画alpha值。
2.4.1 xml实现透明度变化
先看看透明度变化效果图2-7:
首先要做的就是在res下新建anim目录,然后在anim目录下创建m_alpha.xml,以alpha作为根元素进行编写,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromAlpha="1"
android:toAlpha="0" />
编写完xml文件后,在java代码中使用AnimationUtils里的loadAnimation方法加载动画,并使用Animation。
animation = AnimationUtils.loadAnimation(this, R.anim.m_alpha);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (animation !=null) {
img.startAnimation(animation);
}
}
});
2.4.2 纯代码实现透明度变化
构造方法很简单,只有fromAlpha和toAlpha两个参数,代码效果图和图2-7是一样的。
animation =new AlphaAnimation(1.0f,0.0f);
animation.setDuration(1000);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (animation !=null) {
img.startAnimation(animation);
}
}
});
2.5 组合动画
AnimationSet可以将每一个单一的动画组合而成一个动画。如果AnimationSet设置了子对象的属性,那么子对象的属性就会被AnimationSet覆盖(例如duration或fillBefore)。
组合动画特有的属性:
android:shareInterpolator="boolean" //如果为true,所有动画都使用AnimationSet的插值器。否则每个动画使用自己的插值器
在组合动画中,部分AnimationSet的属性添加到AnimationSet会影响自己,有的属性则下推到子对象去使用,还有的属性会被忽略掉,如下:
duration, repeatMode, fillBefore, fillAfter:当这些属性在AnimationSet对象设置时,将会下推到子对象。
repeatCount, fillEnabled:AnimationSet会忽略掉这些属性
startOffset, shareInterpolator:这些属性将会应用到AnimationSet本身
2.5.1 xml实现组合动画
先看看组合动画效果图2-8:
首先要做的就是在res下新建anim目录,然后在anim目录下创建m_set.xml,以set作为根元素进行编写,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
android:duration="2000"
android:fromXScale="1"
android:fromYScale="1"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="2"
android:toYScale="2" />
<rotate
android:duration="2000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="720" />
</set>
编写完xml文件后,在java代码中使用AnimationUtils里的loadAnimation方法加载动画,并使用Animation。
animation = AnimationUtils.loadAnimation(this, R.anim.m_set);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (animation !=null) {
img.startAnimation(animation);
}
}
});
2.5.2 纯代码实现组合动画
纯代码实现旋转需要使用到AnimationSet类。AnimationSet有以下两个个构造方法,一般①不常用,使用②居多。
①AnimationSet(Context context,AttributeSet attrs)
②AnimationSet(boolean shareInterpolator)
构造方法很简单,只有shareInterpolator一个参数,先创建AnimationSet,然后再使用addAnimation添加动画,我添加的是2.4.1的m_alpha和2.1.1的m_rotate动画,代码如下所示,效果图如图2-9。
animation =new AnimationSet(false);
((AnimationSet)animation).addAnimation(AnimationUtils.loadAnimation(this, R.anim.m_alpha));
((AnimationSet)animation).addAnimation(AnimationUtils.loadAnimation(this, R.anim.m_rotate));
animation.setDuration(2000);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (animation !=null) {
img.startAnimation(animation);
}
}
});
2.5.1 fillBefore、fillEnabled的使用
fillBefore适用于AnimationSet动画,fillBefore在是在动画开始之前应用,但是在AnimationSet启动之前这个属性值不会被应用。
先说说要实现的效果,第一种效果是先放大,再从原位置的90°旋转到720°,效果图如图2-10,第二种效果是放大的同时先应用旋转第一帧的属性,接着从旋转属性的第一帧开始旋转,效果图如图2-11。
第一种效果图在xml中的代码如下所示,set 的android:fillBefore="false",scale的 android:fillEnabled="false" ,rotate的android:fillEnabled="true" ,第二章开头说过android:fillBefore是在android:fillEnabled="true"时才会被使用,android:fillBefore=“true”时是在动画开始前应用动画转化(也就是第一帧)。
第二种效果图只是将 rotate中的fillEnabled设置为flase,也就是android:fillEnabled="false"。
编写完xml文件后,在java代码中使用AnimationUtils里的loadAnimation方法加载动画,并使用Animation,java代码和2.5.1中的java代码一样。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillBefore="false">
<scale
android:duration="1000"
android:fromXScale="1"
android:fromYScale="1"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="2"
android:toYScale="2"
android:fillEnabled="false"/>
<rotate
android:duration="2000"
android:fromDegrees="90"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="1000"
android:toDegrees="720"
android:fillEnabled="true" />
</set>