Android 动画animation 深入分析

转载请注明出处:http://blog.csdn.net/farmer_cc/article/details/18259117

Android 动画animation 深入分析

前言:本文试图通过分析动画流程,来理解android动画系统的设计与实现,学习动画的基本原则,最终希望能够指导动画的设计。

0 本文中用到的一些类图

1 view animation

调用方法:view.startAnimation(animation);

[java] view plaincopy

  1. public void startAnimation(Animation animation) {
  2. animation.setStartTime(Animation.START_ON_FIRST_FRAME);
  3. setAnimation(animation);
  4. invalidateParentCaches();
  5. invalidate(true);
  6. }

在invalidate(ture);中

[java] view plaincopy

  1. if (p != null && ai != null) {
  2. final Rect r = ai.mTmpInvalRect;
  3. r.set(0, 0, mRight - mLeft, mBottom - mTop);
  4. // Don‘t call invalidate -- we don‘t want to internally scroll
  5. // our own bounds
  6. p.invalidateChild(this, r);
  7. }

即调用parent的invalidateChild,

假定父控件即为ViewRootImpl;

public final class ViewRootImpl implements ViewParent;

[java] view plaincopy

  1. @Override
  2. public void invalidateChild(View child, Rect dirty) {
  3. invalidateChildInParent(null, dirty);
  4. }
  5. public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
  6. //...省略一堆判断条件,最终调用
  7. if (!mWillDrawSoon && (intersected || mIsAnimating)) {
  8. scheduleTraversals();
  9. }
  10. return null;
  11. }

[java] view plaincopy

  1. void scheduleTraversals() {
  2. if (!mTraversalScheduled) {
  3. mTraversalScheduled = true;
  4. mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
  5. mChoreographer.postCallback(
  6. Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
  7. scheduleConsumeBatchedInput();
  8. }
  9. }

其中mTraversalBarrier = mHandler.getLooper().postSyncBarrier();是设置同步障碍(syncBarrier),当looper中的消息队列执行到barrier 后,会暂停执行,只有当barrier 被释放mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); 后消息队列才能继续执行。

Choreographer mChoreographer; 是动画系统中的核心组织者, 负责统一调度。后面详细说。

[java] view plaincopy

  1. final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
  2. final class TraversalRunnable implements Runnable {
  3. @Override
  4. public void run() {
  5. doTraversal();
  6. }
  7. }

[java] view plaincopy

  1. void doTraversal() {
  2. performTraversals();
  3. }

perform 待补充

[java] view plaincopy

  1. final class ConsumeBatchedInputRunnable implements Runnable {
  2. @Override
  3. public void run() {
  4. doConsumeBatchedInput(mChoreographer.getFrameTimeNanos());
  5. }
  6. }
  7. final ConsumeBatchedInputRunnable mConsumedBatchedInputRunnable =
  8. new ConsumeBatchedInputRunnable();

doConsume 待补充

2 属性动画aninmator

valueAnimator.start();

[java] view plaincopy

  1. private void start(boolean playBackwards) {
  2. if (Looper.myLooper() == null) {
  3. throw new AndroidRuntimeException("Animators may only be run on Looper threads");
  4. }
  5. AnimationHandler animationHandler = getOrCreateAnimationHandler();
  6. animationHandler.mPendingAnimations.add(this);
  7. if (mStartDelay == 0) {
  8. // This sets the initial value of the animation, prior to actually starting it running
  9. setCurrentPlayTime(0);
  10. mPlayingState = STOPPED;
  11. mRunning = true;
  12. notifyStartListeners();
  13. }
  14. animationHandler.start();
  15. }

这里会检查调用线程必须是Looper线程,如果是view相关的属性动画,还必须是UI 线程。

得到AnimationHandle 并把自己加入到PendingAnimations  的list中.

[java] view plaincopy

  1. getOrCreateAnimationHandler();

[java] view plaincopy

  1. protected static ThreadLocal<AnimationHandler> sAnimationHandler =
  2. new ThreadLocal<AnimationHandler>()
  3. protected static class AnimationHandler implements Runnable {
  4. // The per-thread list of all active animations
  5. /** @hide */
  6. protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
  7. // Used in doAnimationFrame() to avoid concurrent modifications of mAnimations
  8. private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();
  9. // The per-thread set of animations to be started on the next animation frame
  10. /** @hide */
  11. protected final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();
  12. /**
  13. * Internal per-thread collections used to avoid set collisions as animations start and end
  14. * while being processed.
  15. * @hide
  16. */
  17. protected final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>();
  18. private final ArrayList<ValueAnimator> mEndingAnims = new ArrayList<ValueAnimator>();
  19. private final ArrayList<ValueAnimator> mReadyAnims = new ArrayList<ValueAnimator>();
  20. private final Choreographer mChoreographer;
  21. private boolean mAnimationScheduled;

AnimationHandler 就是一个runnable, 注意成员变量中的多个animator 的list 以及重要的mChoreographer = Choreographer.getInstance();

mChoreographer 也是一个threadlocal的变量。

在animationHandler.start() 中

[java] view plaincopy

  1. public void start() {
  2. scheduleAnimation();
  3. }
  4. private void scheduleAnimation() {
  5. if (!mAnimationScheduled) {
  6. mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
  7. mAnimationScheduled = true;
  8. }
  9. }

this 是runnable 即把animationHandler自己添加添加到mChoreographer 的队列中。

[java] view plaincopy

  1. public void postCallback(int callbackType, Runnable action, Object token) {
  2. postCallbackDelayed(callbackType, action, token, 0);
  3. }
  4. public void postCallbackDelayed(int callbackType,
  5. Runnable action, Object token, long delayMillis) {
  6. postCallbackDelayedInternal(callbackType, action, token, delayMillis);
  7. }
  8. private void postCallbackDelayedInternal(int callbackType,
  9. Object action, Object token, long delayMillis) {
  10. synchronized (mLock) {
  11. final long now = SystemClock.uptimeMillis();
  12. final long dueTime = now + delayMillis;
  13. mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
  14. if (dueTime <= now) {
  15. scheduleFrameLocked(now);
  16. } else {
  17. Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
  18. msg.arg1 = callbackType;
  19. msg.setAsynchronous(true);
  20. mHandler.sendMessageAtTime(msg, dueTime);
  21. }
  22. }
  23. }

传入的delay为0, 即调用scheduleFrameLocked(now);

[java] view plaincopy

  1. private void scheduleFrameLocked(long now) {
  2. if (!mFrameScheduled) {
  3. mFrameScheduled = true;
  4. if (USE_VSYNC) {
  5. if (DEBUG) {
  6. Log.d(TAG, "Scheduling next frame on vsync.");
  7. }
  8. // If running on the Looper thread, then schedule the vsync immediately,
  9. // otherwise post a message to schedule the vsync from the UI thread
  10. // as soon as possible.
  11. if (isRunningOnLooperThreadLocked()) {
  12. scheduleVsyncLocked();
  13. } else {
  14. Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
  15. msg.setAsynchronous(true);
  16. mHandler.sendMessageAtFrontOfQueue(msg);
  17. }
  18. } else {
  19. final long nextFrameTime = Math.max(
  20. mLastFrameTimeNanos / NANOS_PER_MS + sFrameDelay, now);
  21. if (DEBUG) {
  22. Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
  23. }
  24. Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
  25. msg.setAsynchronous(true);
  26. mHandler.sendMessageAtTime(msg, nextFrameTime);
  27. }
  28. }
  29. }

[java] view plaincopy

  1. private static final boolean USE_VSYNC = SystemProperties.getBoolean(
  2. "debug.choreographer.vsync", true);

USE_VSYNC 默认是true;

[java] view plaincopy

  1. private boolean isRunningOnLooperThreadLocked() {
  2. return Looper.myLooper() == mLooper;
  3. }

检查当前looper和mChoreographer的looper是否一致。一般情况是一致的。就会调用scheduleVsyncLocked();

[java] view plaincopy

  1. private void scheduleVsyncLocked() {
  2. mDisplayEventReceiver.scheduleVsync();
  3. }

[java] view plaincopy

  1. public void scheduleVsync() {
  2. if (mReceiverPtr == 0) {
  3. Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
  4. + "receiver has already been disposed.");
  5. } else {
  6. nativeScheduleVsync(mReceiverPtr);
  7. }
  8. }

到了native 暂时先不涉及。

回头来看animationHandler 的run()。 前面提到animationHandler把自己添加到mChoreographer,当被调用时,调用run方法。

[java] view plaincopy

  1. // Called by the Choreographer.
  2. @Override
  3. public void run() {
  4. mAnimationScheduled = false;
  5. doAnimationFrame(mChoreographer.getFrameTime());
  6. }

[java] view plaincopy

  1. public long getFrameTime() {
  2. return getFrameTimeNanos() / NANOS_PER_MS;
  3. }
  4. public long getFrameTimeNanos() {
  5. synchronized (mLock) {
  6. if (!mCallbacksRunning) {
  7. throw new IllegalStateException("This method must only be called as "
  8. + "part of a callback while a frame is in progress.");
  9. }
  10. return USE_FRAME_TIME ? mLastFrameTimeNanos : System.nanoTime();
  11. }
  12. }

doAnimationFrame()总结就是

1.遍历pending list动画,如果delay为0 则调用start,不为0,加入delay list;

2.遍历delay list, 根据frametime计算是继续delay还是ready可以播放,若是ready,则加入到ready list中;

3 遍历ready list,调用start ;

4,遍历所有animation,根据frametime计算动画是否要结束,如果可以结束,则加入到ending list中;

5,遍历ending list, 调用end;

6, 如果有列表中仍然有动画,则继续scheduleAnimation;

[java] view plaincopy

  1. private void doAnimationFrame(long frameTime) {
  2. // mPendingAnimations holds any animations that have requested to be started
  3. // We‘re going to clear mPendingAnimations, but starting animation may
  4. // cause more to be added to the pending list (for example, if one animation
  5. // starting triggers another starting). So we loop until mPendingAnimations
  6. // is empty.
  7. while (mPendingAnimations.size() > 0) {
  8. ArrayList<ValueAnimator> pendingCopy =
  9. (ArrayList<ValueAnimator>) mPendingAnimations.clone();
  10. mPendingAnimations.clear();
  11. int count = pendingCopy.size();
  12. for (int i = 0; i < count; ++i) {
  13. ValueAnimator anim = pendingCopy.get(i);
  14. // If the animation has a startDelay, place it on the delayed list
  15. if (anim.mStartDelay == 0) {
  16. anim.startAnimation(this);
  17. } else {
  18. mDelayedAnims.add(anim);
  19. }
  20. }
  21. }
  22. // Next, process animations currently sitting on the delayed queue, adding
  23. // them to the active animations if they are ready
  24. int numDelayedAnims = mDelayedAnims.size();
  25. for (int i = 0; i < numDelayedAnims; ++i) {
  26. ValueAnimator anim = mDelayedAnims.get(i);
  27. if (anim.delayedAnimationFrame(frameTime)) {
  28. mReadyAnims.add(anim);
  29. }
  30. }
  31. int numReadyAnims = mReadyAnims.size();
  32. if (numReadyAnims > 0) {
  33. for (int i = 0; i < numReadyAnims; ++i) {
  34. ValueAnimator anim = mReadyAnims.get(i);
  35. anim.startAnimation(this);
  36. anim.mRunning = true;
  37. mDelayedAnims.remove(anim);
  38. }
  39. mReadyAnims.clear();
  40. }
  41. // Now process all active animations. The return value from animationFrame()
  42. // tells the handler whether it should now be ended
  43. int numAnims = mAnimations.size();
  44. for (int i = 0; i < numAnims; ++i) {
  45. mTmpAnimations.add(mAnimations.get(i));
  46. }
  47. for (int i = 0; i < numAnims; ++i) {
  48. ValueAnimator anim = mTmpAnimations.get(i);
  49. if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
  50. mEndingAnims.add(anim);
  51. }
  52. }
  53. mTmpAnimations.clear();
  54. if (mEndingAnims.size() > 0) {
  55. for (int i = 0; i < mEndingAnims.size(); ++i) {
  56. mEndingAnims.get(i).endAnimation(this);
  57. }
  58. mEndingAnims.clear();
  59. }
  60. // If there are still active or delayed animations, schedule a future call to
  61. // onAnimate to process the next frame of the animations.
  62. if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
  63. scheduleAnimation();
  64. }
  65. }

在animationFrame() 中根据当前状态,并且计算fraction,调用animateValue();

[java] view plaincopy

  1. boolean animationFrame(long currentTime) {
  2. boolean done = false;
  3. switch (mPlayingState) {
  4. case RUNNING:
  5. case SEEKED:
  6. //省略计算fraction的代码
  7. animateValue(fraction);
  8. break;
  9. }
  10. return done;
  11. }

通过mInterpolator.getInterpolation计算fraction;@Interpolator

根据fraction计算内部所有value,如果有updateListener,调用之。

[java] view plaincopy

  1. void animateValue(float fraction) {
  2. fraction = mInterpolator.getInterpolation(fraction);
  3. mCurrentFraction = fraction;
  4. int numValues = mValues.length;
  5. for (int i = 0; i < numValues; ++i) {
  6. mValues[i].calculateValue(fraction);
  7. }
  8. if (mUpdateListeners != null) {
  9. int numListeners = mUpdateListeners.size();
  10. for (int i = 0; i < numListeners; ++i) {
  11. mUpdateListeners.get(i).onAnimationUpdate(this);
  12. }
  13. }
  14. }

3. 插值器

从上面的介绍可以看到,Interpolator的关键是getInterpolation();

在ValueAnimator.animationFrame()中可以看到, 传递给Interpolator 的fraction是在[0,1] 值域范围。

[java] view plaincopy

  1. float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
  2. if (fraction >= 1f) {
  3. if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
  4. // Time to repeat
  5. if (mListeners != null) {
  6. int numListeners = mListeners.size();
  7. for (int i = 0; i < numListeners; ++i) {
  8. mListeners.get(i).onAnimationRepeat(this);
  9. }
  10. }
  11. if (mRepeatMode == REVERSE) {
  12. mPlayingBackwards = !mPlayingBackwards;
  13. }
  14. mCurrentIteration += (int)fraction;
  15. fraction = fraction % 1f;
  16. mStartTime += mDuration;
  17. } else {
  18. done = true;
  19. fraction = Math.min(fraction, 1.0f);
  20. }
  21. }
  22. if (mPlayingBackwards) {
  23. fraction = 1f - fraction;
  24. }

所以设计Interpolator 就是设计一个输入[0,1] 的函数。

先参观一下系统的几个Interpolator。

3.1 AccelerateDecelerateInterpolator

cos(t+1)Pi /2 +0.5f

从图可以看到,先加速后减速,病最终到达结束位置。

[java] view plaincopy

  1. public class AccelerateDecelerateInterpolator implements Interpolator {
  2. public float getInterpolation(float input) {
  3. return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
  4. }
  5. }

3.2 AccelerateInterpolator

如果factor=1 则函数为x^2

否则函数为x^a (a 是参数)

默认函数式x^2

如图示,逐渐加速到结束位置。

[java] view plaincopy

  1. public class AccelerateInterpolator implements Interpolator {
  2. private final float mFactor;
  3. private final double mDoubleFactor;
  4. public AccelerateInterpolator() {
  5. mFactor = 1.0f;
  6. mDoubleFactor = 2.0;
  7. }
  8. /**
  9. * Constructor
  10. *
  11. * @param factor Degree to which the animation should be eased. Seting
  12. *        factor to 1.0f produces a y=x^2 parabola. Increasing factor above
  13. *        1.0f  exaggerates the ease-in effect (i.e., it starts even
  14. *        slower and ends evens faster)
  15. */
  16. public AccelerateInterpolator(float factor) {
  17. mFactor = factor;
  18. mDoubleFactor = 2 * mFactor;
  19. }
  20. public float getInterpolation(float input) {
  21. if (mFactor == 1.0f) {
  22. return input * input;
  23. } else {
  24. return (float)Math.pow(input, mDoubleFactor);
  25. }
  26. }
  27. }

3.3 LinearInterpolator

线性的就是Y=X 没啥说的。

[java] view plaincopy

  1. public class LinearInterpolator implements Interpolator {
  2. public float getInterpolation(float input) {
  3. return input;
  4. }
  5. }

3.4 anticipateInterpolator

函数是:x^2((a+1)x-a) 默认参数a=2 默认函数为x^2(3x-1)

如图示, 会先反方向执行一段,然后正向一直加速至结束位置。

[java] view plaincopy

  1. public class AnticipateInterpolator implements Interpolator {
  2. private final float mTension;
  3. public AnticipateInterpolator() {
  4. mTension = 2.0f;
  5. }
  6. /**
  7. * @param tension Amount of anticipation. When tension equals 0.0f, there is
  8. *                no anticipation and the interpolator becomes a simple
  9. *                acceleration interpolator.
  10. */
  11. public AnticipateInterpolator(float tension) {
  12. mTension = tension;
  13. }
  14. public float getInterpolation(float t) {
  15. // a(t) = t * t * ((tension + 1) * t - tension)
  16. return t * t * ((mTension + 1) * t - mTension);
  17. }
  18. }

3.5 aniticipateOvershoot

是一个分段函数,默认参数a=3

2x*x[(2x*(a+1)-a)]     0<=x<=0.5

2(x-1)(x-1)[(2x-1)(a+1)+a]    0.5<x<=1

通过下图可以看到,动画会先反方向执行,然后向正方向逐渐加速,在快结束时逐渐减速,并超过预设的值,最后回到结束位置。

2x*x[(2x*(a+1)-a)]     0<=x<=0.5 的函数图

2(x-1)(x-1)[(2x-1)(a+1)+a]    0.5<x<=1的函数图

[java] view plaincopy

  1. public class AnticipateOvershootInterpolator implements Interpolator {
  2. private final float mTension;
  3. public AnticipateOvershootInterpolator() {
  4. mTension = 2.0f * 1.5f;
  5. }
  6. /**
  7. * @param tension Amount of anticipation/overshoot. When tension equals 0.0f,
  8. *                there is no anticipation/overshoot and the interpolator becomes
  9. *                a simple acceleration/deceleration interpolator.
  10. */
  11. public AnticipateOvershootInterpolator(float tension) {
  12. mTension = tension * 1.5f;
  13. }
  14. /**
  15. * @param tension Amount of anticipation/overshoot. When tension equals 0.0f,
  16. *                there is no anticipation/overshoot and the interpolator becomes
  17. *                a simple acceleration/deceleration interpolator.
  18. * @param extraTension Amount by which to multiply the tension. For instance,
  19. *                     to get the same overshoot as an OvershootInterpolator with
  20. *                     a tension of 2.0f, you would use an extraTension of 1.5f.
  21. */
  22. public AnticipateOvershootInterpolator(float tension, float extraTension) {
  23. mTension = tension * extraTension;
  24. }
  25. private static float a(float t, float s) {
  26. return t * t * ((s + 1) * t - s);
  27. }
  28. private static float o(float t, float s) {
  29. return t * t * ((s + 1) * t + s);
  30. }
  31. public float getInterpolation(float t) {
  32. // a(t, s) = t * t * ((s + 1) * t - s)
  33. // o(t, s) = t * t * ((s + 1) * t + s)
  34. // f(t) = 0.5 * a(t * 2, tension * extraTension), when t < 0.5
  35. // f(t) = 0.5 * (o(t * 2 - 2, tension * extraTension) + 2), when t <= 1.0
  36. if (t < 0.5f) return 0.5f * a(t * 2.0f, mTension);
  37. else return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f);
  38. }
  39. }

4. 指导设计动画。

从第3节中可以看到,想要让动画按照我们预期的行为来执行,需要做的就是找到合适的函数。

画图使用http://www.fooplot.com/在线工具

时间: 2024-11-08 22:06:37

Android 动画animation 深入分析的相关文章

Android动画Animation的两种加载执行方式

本文以简单的AlphaAnimation("淡入淡出(透明度改变)"动画)为例,简单的说明Android动画Animation的两种加载执行方法: (1) 直接写Java代码,制作Android动画. (2) 写XML配置文件,加载XML资源文件执行. 其实这两者是一致的.要知道,在Android中,凡是可以在XML文件完成的View,代码亦可完全写出来. 现在先给出一个Java代码完成的动画AlphaAnimation,AlphaAnimation功能简单,简言之,可以让一个View

【转载】Android动画Animation

原文链接:http://www.cnblogs.com/zxl-jay/archive/2011/10/03/2198632.html 今天学习了Android中的Animation,它是一种能为我们提供动画效果的类.借助于网络资源和自己的理解,我将今天学到的知识总结如下(内容有点长,但是你读完后绝对对你有帮助,学习就得有点耐心): Android提供了Animation来实现动画的效果,在Android SDK介绍了2种Animation模式: 1. Tween Animation:通过对场景

Android动画Animation简单示例

Animation是Android给我们提供的一个可以实现动画效果的API,利用Animation我们可以实现一系列的动画效果,比如缩放动画,透明度动画,旋转动画,位移动画,布局动画,帧动画等等.Android基础动画分为四种,分别是Tween Animation,Frame Animation,Layout Animation,Property Animation.由于动画效果不好用图片进行展示,大家可以自己亲手试试看下效果.下面给出前三种简单的示例代码,帮助大家进行简单的动画入门. (1)

(三十九)android动画 Animation四大属性 详解(转载:http://www.android100.org/html/201304/25/2295.html)

一.Animation主要有四大属性,分别是淡入淡出,绕轴旋转,变化大小,位移变化 二.四大属性的共同的方法 1.setDuration(long durationMills):设置动画持续的时间(单位:毫秒) 2.setFillAfter(boolean fillAfter):如果fillAfter的值为true,则动画执行后看,控件将停留在执行结束的状态 3.setFillBefore(boolean fillBefore):如果fillBefore的值为true,则动画执行后看,控件将停留

Android动画的深入分析

一.AnimationDrawable的使用 详见:Drawable类及XMLDrawable的使用 补充:通过Animation的setAnimationListener()可以给View动画添加监听过程 二.自定义View动画(略,以后有空补上) 三.帧动画 定义:顺序播放一组预先定义好的图片. 使用:①.在res/drawable/中定义<animation-list>标签 ②.在java代码中start使用 <animation-list> <item android

android 动画animation

一.动画类型 Android的animation由四种类型组成:alpha.scale.translate.rotate XML配置文件中 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画效果 translate 画面转换位置移动动画效果 rotate 画面转移旋转动画效果 Java Code代码中 AlphaAnimation 渐变透明度动画效果 ScaleAnimation 渐变尺寸伸缩动画效果 TranslateAnimation 画面转换位置移动动画效果 RotateAnim

Android动画效果

Android动画 Animation动画 - Frame动画 - Tween动画 通常电影里面的画面持续时间长为一秒24帧 直接看代码吧 package com.example.myanimotor; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.content.Intent; import android.support.v7.app

Android开发艺术探索——第七章:Android动画深入分析

Android开发艺术探索--第七章:Android动画深入分析 Android的动画可以分成三种,view动画,帧动画,还有属性动画,其实帧动画也是属于view动画的一种,,只不过他和传统的平移之类的动画不太一样的是表现形式上有点不一样,view动画是通过对场景的不断图像交换而产生的动画效果,而帧动画就是播放一大段图片,很显然,图片多了会OOM,属性动画通过动态的改变对象的属性达到动画效果,也是api11的新特性,在低版本无法使用属性动画,但是我们依旧有一些兼容库,OK,我们还是继续来看下详细

Android(五)动画Animation

第一种:代码格式: package com.itzb.annimation; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.view.animation.AlphaAnimation; import android.v