说起动画大家想必都比较熟悉,因为开发中要经常面对各种各样的动画效果。如果遇到一个比较“挑剔”的设计师,在自定义控件和动画方面没有两把像样的刷子估计就要受尽折磨了。自定义控件除了一些View相关知识比较重要外,动画也占相当重要的部分。Android自发布到现在经历了多次重大版本迭代,UI效果已经变的相当强大,当年从诺基亚N73过渡到Android手机,感觉换了一片天地,其强大的功能和酷炫的界面令人折服,但这种酷炫跟今天的手机来比,估计要“差之千里”了。言归正传,今天主要讲Android的动画,在Android3.0之前,动画主要分为两类:补间动画和帧动画。Android3.0发布之后,属性动画正式登场,可以说是开启了动画的新纪元。除了这3种动画外,还出现了一些在特定领域使用的动画,如转场动画,触摸反馈动画等。本文的重点是Android的三大主要动画,因为篇幅较长分为了两篇,第一篇主要内容为补间动画和帧动画,第二篇——Android动画详解(下)则集中精力介绍属性动画。
补间动画
补间动画在Android早期版本上可以说是“一手遮天”,但随着属性动画的发布地位就明显下降了。补间动画在现在的开发中应用的已经较少,因为其只对View起作用,而且View的位置并未更改,很容易导致其点击事件响应错位。虽然补间动画确定明显,但是一些简单的场景还是可以一试的,毕竟用起来方便。
补间动画主要可以实现平移、缩放、旋转、透明度变化4种基本动画以及其组合实现的组合动画。如下图所示:
图中所说的位置既可能是坐标,也可能是百分比。比如说pivotX为50时代表作为为50,pivotX为0.5或者50%时则该图形的X方向的中心,如果是50P%则代表父布局的X方向的中心。
上图为补间动画的一个简单实现,其XML代码如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="5000"
android:fillAfter="true"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:shareInterpolator="true">
<translate
android:fromXDelta="0%"
android:fromYDelta="0%"
android:toXDelta="100%"
android:toYDelta="100%" />
<scale
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0"
android:toYScale="0" />
<rotate
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360" />
<alpha
android:fromAlpha="1.0"
android:toAlpha="0" />
</set>
其中:
- android:duration——动画持续时间
- android:fillAfter——是否保留动画结束时的效果
- android:interpolator——插值器设置
- android:shareInterpolator——是否共享插值器
插值器主要用于控制动画的变化速率,Android提供了多种插值器供我们选择。如:
- LinearInterpolator——线性插值器
- AccelerateInterpolator——加速插值器
- DecelerateInterpolator——减速插值器
- AccelerateDecelerateInterpolator——加减速插值器
- CycleInterpolator——正弦加速器
帧动画
帧动画的使用比较简单,只要有合理的素材就可以达到自己想要的效果。帧动画的核心是AnimationDrawable类,它的方法也比较简单。
- start()——开始播放动画
- stop()——在当前帧结束动画
- isRunning()——是否正在播放
- getNumberOfFrames()——获得所有的帧数
- getFrame(int index)——获得对应索引的帧
- getDuration(int i)——获得对应索引帧的持续时间
- isOneShot()——是否仅播放一次
- setOneShot(boolean oneShot)——设置是否仅播放一次
- addFrame(@NonNull Drawable frame, int duration)——动态添加一帧,并设置该帧的持续时间
下面为一个帧动画的效果图,其中左侧的动画使用的是XML方式,右侧的动画使用的是Java的方式。
XML的配置代码如下:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
<item android:drawable="@mipmap/zuan1" android:duration="200"/>
<item android:drawable="@mipmap/zuan2" android:duration="200"/>
<item android:drawable="@mipmap/zuan3" android:duration="200"/>
<item android:drawable="@mipmap/zuan4" android:duration="200"/>
<item android:drawable="@mipmap/zuan5" android:duration="200"/>
<item android:drawable="@mipmap/zuan6" android:duration="200"/>
<item android:drawable="@mipmap/zuan7" android:duration="200"/>
<item android:drawable="@mipmap/zuan8" android:duration="200"/>
<item android:drawable="@mipmap/zuan9" android:duration="200"/>
</animation-list>
Java方式(包括了xml方式的调用)的代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_frame_animation);
mXmlImg = this.findViewById(R.id.frame_xml_img);
mJavaImg = this.findViewById(R.id.frame_java_img);
final AnimationDrawable xmlDrawable = (AnimationDrawable) getResources().getDrawable(R.drawable.frameanimation);
mXmlImg.setImageDrawable(xmlDrawable);
final AnimationDrawable javaDrawable = new AnimationDrawable();
javaDrawable.setOneShot(false);//是否执行1次
int resId;
for (int i = 1; i < 12; i++) {
resId = getResources().getIdentifier("hj" + i, "mipmap", getPackageName());
javaDrawable.addFrame(getResources().getDrawable(resId), 200);
}
mJavaImg.setImageDrawable(javaDrawable);
findViewById(android.R.id.content).post(new Runnable() {
@Override
public void run() {
xmlDrawable.start();
javaDrawable.start();
}
});
}
注意,之所以start方法要在post里面调用是因为onCreate()中,对应的AnimationDrawable可能还未附着到Window上。另外我们可以在onWindowFocusChange()或者onResume()方法中调用。
项目代码见:Android动画详解(下)