Android中的Drawable和动画

Android中Drawable是一种可以在Canvas上进行绘制抽象的概念,种类很多,常见的颜色和图片都可以是一个Drawable。Drawable有很多种,它们表示一种图像的概念,但是它们又不全是图片,通过颜色也可以构造出各式各样的图像的效果。

Drawable的分类

  • BitmapDrawable。它表示的就是一张图片,在实际开发中我们可以直接引用原始的图片即可。
  • ShapeDrawable。可以理解为通过颜色来构造图形,可以有纯色的图形,也可以具有渐变效果的图形。
  • LayerDrawable。对应的标签是,它表示一种层次化的Drawable集合,通过将不同的Drawable放置在不同的层上面从而达到一种叠加后的效果。
  • StateListDrawable。对应于,它表示Drawable集合,每个Drawable都对应着View的一种状态,这样系统就会根据View的状态来选择合适的Drawable。
  • LeveListDrawable。对应于标签,表示一个Drawable集合,集合中的每个Drawable都有一个等级Level的概念。
  • InsetDrawable。对应于标签,它可以将其他的Drawable内嵌到自己当中,并可以在四周留出一定的间距。
  • ScaleDrawable。对应于,它可以根据自己的等级(level)将指定的Drawable缩放到一定比例。
  • ClipDrawable。对应于,它可以根据自己当前的等级(level)来裁剪另一个Drawable,裁剪方向可以通过android:clipOrientation和android:gravity这两个属性来共同控制。

Android动画

Android的动画可以分为三种:View动画、帧动画和属性动画。View动画通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,它是一种渐近式动画,并且View动画支持自定义。帧动画通过顺序播放一系列图像从而产生动画效果,可以简单理解为图片切换动画。属性动画通过动态地改变对象的属性从而达到动画效果,属性动画为API 11的新特性,在低版本无法直接使用属性动画,但我们仍然可以通过兼容库来使用它。

View动画

View动画的作用对象是View,它支持平移动画、缩放动画、旋转动画和透明度动画。有四个子类:TranslateAnimation,ScaleAnimation,RotateAnimation和AlphaAnimation。可以通过XML来定义。比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:interpolator="@android:anim/accelerate_interpolator"
android:shareInterpolator="true" >

<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0" />

<translate
android:fromYDelta="500"
android:toXDelta="0" />

</set>

帧动画

帧动画是顺序播放一组预先定义好的图片,类似于电影播放。不同于View动画,系统提供了另外一个类AnimationDrawable来使用帧动画。虽然比较简单,但是容易引起OOM,所以在使用帧动画时应尽量避免使用过多尺寸较大的图片。

属性动画

属性动画中有ValueAnimator、ObjectAnimator和AnimatorSet等概念,通过它们可以实现绚丽的动画。属性动画可以对任意对象的属性进行动画而不仅仅是View,动画默认时间间隔300ms,默认帧率10ms/帧。在一个时间间隔内完成对象从一个属性值到另一个属性值的改变,因此属性动画几乎是无所不能,只要对象有这个属性,它都能实现动画效果。

有个开源动画库:nineoldandroids来兼容之前的版本,因为属性动画是从API 11开始才有的。比较常用的动画类ValueAnimator、ObjectAnimator和AnimatorSet,其中ObjectAnimator继承ValueAnimator,AnimatorSet是动画集合,可以定义一组动画,它们使用起来也是极其简单的。如何使用呢:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void performAnimate(final View target, final int start, final int end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {

// 持有一个IntEvaluator对象,方便下面估值的时候使用
private IntEvaluator mEvaluator = new IntEvaluator();

@Override
public void onAnimationUpdate(ValueAnimator animator) {
// 获得当前动画的进度值,整型,1-100之间
int currentValue = (Integer) animator.getAnimatedValue();
Log.d(TAG, "current value: " + currentValue);

// 获得当前进度占整个动画过程的比例,浮点型,0-1之间
float fraction = animator.getAnimatedFraction();
// 直接调用整型估值器通过比例计算出宽度,然后再设给Button
target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
target.requestLayout();
}
});

valueAnimator.setDuration(5000).start();
}

使用动画注意点

  • OOM问题。这个主要是出现在帧动画中,当图片数量较多且图片较大时就极易出现OOM。
  • 内存泄漏。在属性动画中有一类无限循环的动画,这类动画需要在Activity退出时及时停止,否则将导致Activity无法释放从而造成内存泄漏,View动画则并不存在此问题。
  • 兼容性问题。动画在3.0以下的系统上有兼容性问题,在某些特殊场景可能无法正常工作,因此要做好适配问题。
  • View动画的问题。View动画是对View的影像做动画,并不是真正改变View的状态,因此有时候会出现动画完成后View无法隐藏的现象,即setVisibility(View.GONE)失效了,这个时候只要调用View.clearAnimation()清除View动画即可解决此问题。
  • 不要使用PX。在进行动画的过程中,要尽量使用dp,使用px会导致在不同设备上有不同的效果。
  • 动画元素的交互。将View移动(平移)后,在Android 3.0以前的系统上,不管是View动画还是属性动画,新位置均无法触发单击事件,同时,老位置仍然可以触发单击事件。尽管View已经在视觉上不存在了,将View移回原位置以后,原位置的单击事件继续生效。从3.0开始,属性动画的单击事件触发位置为移动后的位置,但是View动画仍然在原位置。
  • 硬件加速。使用动画过程中,建议开启硬件加速,这样会提高动画的流畅性。
,