您的浏览器不支持CSS3,建议使用Firfox、Chrome等浏览器,以取得最佳显示效果

Android Animation完全总结(一) Drawable动画

Android 1,072℃ 0 1年前 (2016-06-03)

本文对Android中的常用动画技术进行了较为全面的总结,并给出了代码示例(Java代码实现和XML中实现)。由于内容较多,所以尽可能简洁表述,并重点指出其中不易理解、容易出错的内容。

本文提到的Android动画主要有三类:

  • Drawable动画
  • Animation与AnimationSet
  • Animator与AnimatorSet

示例代码、思维导图可在此下载
https://github.com/jzj1993/AndroidAnimation

Animatable Drawable 动画

用Drawable实现动画,适用于不需要变换View,只需要对View中所展示的Drawable图形产生动画的情况。

由于不像Animation需要对View进行矩阵变换,更不需要像Animator使用反射机制,实现同样的效果,Drawable动画通常性能较好,推荐使用。

原理简介

当给一个View的背景、ImageView的src设置了Drawable后,View会将自身设置为Drawable的Drawable.Callback(View实现了Drawable.Callback接口)。于是在Drawable需要刷新时,可通过这个接口调用View的invalidate,从而触发View.onDraw方法进行重绘。具体可参考Android源码。

支持动画的Drawable,应实现Animatable接口。有些View属性会对Drawable进行判断(例如ImageView的src属性),如果实现了Animatable接口,就会自动调用其start方法启动动画。

  1. public class ImageView extends View implements Drawable.Callback {
  2. public void setImageDrawable(Drawable drawable) {
  3. if (mDrawable != drawable) {
  4. // ...
  5. updateDrawable(drawable);
  6. // ...
  7. }
  8. }
  9. private void updateDrawable(Drawable d) {
  10. if (mDrawable != null) {
  11. mDrawable.setCallback(null);
  12. unscheduleDrawable(mDrawable);
  13. }
  14. mDrawable = d;
  15. if (d != null) {
  16. d.setCallback(this);
  17. if (d.isStateful()) {
  18. d.setState(getDrawableState());
  19. }
  20. d.setLevel(mLevel);
  21. d.setLayoutDirection(getLayoutDirection());
  22. d.setVisible(getVisibility() == VISIBLE, true);
  23. mDrawableWidth = d.getIntrinsicWidth();
  24. mDrawableHeight = d.getIntrinsicHeight();
  25. applyColorMod();
  26. configureBounds();
  27. } else {
  28. mDrawableWidth = mDrawableHeight = -1;
  29. }
  30. }
  31. }

FrameAnimation 逐帧动画(AnimationDrawable)

逐帧动画实际上就是一种支持动画效果的Drawable

  1. public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable { }

从XML创建,使用Java代码加载:

  • 将每一帧的图片放在资源文件夹res/drawable

  • 在XML中定义动画每一帧及其持续时间

  • 在Java代码中加载动画并设置给View,然后启动动画

  • OneShot属性为true则只播放一次,否则不断循环播放

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:oneshot="false">
  4. <item
  5. android:drawable="@drawable/icon1"
  6. android:duration="200" />
  7. <item
  8. android:drawable="@drawable/icon2"
  9. android:duration="200" />
  10. <item
  11. android:drawable="@drawable/icon3"
  12. android:duration="200" />
  13. <item
  14. android:drawable="@drawable/icon4"
  15. android:duration="200" />
  16. </animation-list>
  1. mFrameAnimation = (AnimationDrawable) getResources().getDrawable(R.drawable.frame_anim); // 从XML加载动画
  2. mTextView.setBackground(mFrameAnimation);
  3. mFrameAnimation.start();
  • 也可以在Java代码中实例化AnimationDrawable对象,并添加帧和持续时间
  1. mFrameAnimation = new AnimationDrawable();
  2. mFrameAnimation.addFrame(getResources().getDrawable(R.drawable.icon1), 200);
  3. mFrameAnimation.addFrame(getResources().getDrawable(R.drawable.icon2), 200);

注意:

Android系统提供的这种逐帧动画,通常每一帧是一个BitmapDrawable,会在内存中一直保存每一帧的Bitmap,如果帧数较多、每一帧图片较大,消耗的内存会很大。

如果对内存有要求,可以自行实现逐帧动画,每切换一帧的时候临时加载该帧的BitmapDrawable,这样虽然增加了一些CPU资源消耗,但减少了内存占用。

AnimatedRotateDrawable旋转动画

和逐帧动画类似,Android系统还提供了AnimatedRotateDrawable,可以实现图片旋转的效果,常用于展示进度条动画。

由于构造函数是私有的,AnimatedRotateDrawable不支持在Java代码中实例化,只能从XML加载。

res/drawable/loading.png是一个PNG文件

res/drawable/animated_rotate.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:drawable="@drawable/loading"
  4. android:fromDegrees="0.0"
  5. android:pivotX="50.0%"
  6. android:pivotY="50.0%"
  7. android:toDegrees="360.0" />

注:因为是Drawable,animated_rotate的XML文件应该放在drawable文件夹下。

在XML中引用Drawable即可

  1. <ProgressBar
  2. android:id="@+id/progress"
  3. android:layout_width="50dp"
  4. android:layout_height="50dp"
  5. android:indeterminate="true"
  6. android:indeterminateDrawable="@drawable/animated_rotate" />
  7. <ImageView
  8. android:id="@+id/image_view"
  9. android:layout_width="50dp"
  10. android:layout_height="50dp"
  11. android:layout_marginTop="60dp"
  12. android:src="@drawable/animated_rotate" />

需要注意的是,不同的View和属性,是否会自动启动Drawable动画的行为不同。例如ProgressBar从XML加载indeterminateDrawable是可以启动动画的,但从Java加载则需要显示调用start方法。而ImageView的src属性,则可以自动启动动画。

  • 如果用Java代码给ProgressBar的indeterminateDrawable设置Drawable,一般需要用setBounds指定drawable的宽高,并显示调用Animatable.start()方法启动动画。

  • 如果用Java代码给ImageView的src设置Drawable,ImageView会自动处理Drawable的尺寸,并判断其是否实现了Animatable接口,从而启动动画。

  1. ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress);
  2. final Drawable drawable = getResources().getDrawable(R.drawable.animated_rotate);
  3. if (drawable != null) {
  4. drawable.setBounds(0, 0, progressBar.getWidth(), progressBar.getHeight());
  5. progressBar.setIndeterminateDrawable(drawable);
  6. if (drawable instanceof Animatable) {
  7. ((Animatable) drawable).start();
  8. }
  9. }
  10. ImageView imageView = (ImageView) findViewById(R.id.image_view);
  11. imageView.setImageDrawable(getResources().getDrawable(R.drawable.animated_rotate));

自定义DrawableAnimation

通过实现Animatable接口,可以自行定义支持动画效果的Drawable。

示例代码是一个每隔0.5s切换一种随机颜色值的Drawable,具体效果可参考工程源码。

  1. public class MyAnimDrawable extends Drawable implements Animatable, Runnable {
  2. private boolean mRunning = false;
  3. @Override
  4. public void draw(Canvas canvas) {
  5. canvas.drawARGB(128, (int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
  6. }
  7. @Override
  8. public void setAlpha(int alpha) {
  9. }
  10. @Override
  11. public void setColorFilter(ColorFilter cf) {
  12. }
  13. @Override
  14. public int getOpacity() {
  15. return 0;
  16. }
  17. @Override
  18. public boolean setVisible(boolean visible, boolean restart) {
  19. boolean changed = super.setVisible(visible, restart);
  20. if (visible) {
  21. if (changed || restart) {
  22. nextFrame();
  23. }
  24. } else {
  25. unscheduleSelf(this);
  26. }
  27. return changed;
  28. }
  29. @Override
  30. public void start() {
  31. if (!mRunning) {
  32. mRunning = true;
  33. nextFrame();
  34. }
  35. }
  36. @Override
  37. public void stop() {
  38. unscheduleSelf(this);
  39. mRunning = false;
  40. }
  41. @Override
  42. public boolean isRunning() {
  43. return mRunning;
  44. }
  45. @Override
  46. public void run() {
  47. invalidateSelf();
  48. nextFrame();
  49. }
  50. private void nextFrame() {
  51. unscheduleSelf(this);
  52. scheduleSelf(this, SystemClock.uptimeMillis() + 500);
  53. }
  54. }
来自为知笔记(Wiz)

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

0

暂无评论

评论前:需填写以下信息,或 登录

用户登录

忘记密码?