1. 程式人生 > >Android動畫原理

Android動畫原理

一個動畫重要的東西就四個部分,開始時間,結束時間,做什麼,如何做。上述是一個imageView在x方向移動的動畫,可以看到它符合上面所說的四個部分,做什麼:沿x方向移動100如何做:直線且不斷加速開始時間:startAnimtion呼叫的那一刻結束時間: 開始時間+500從上面也大致瞭解到補間動畫的框架是通過先建立一個Animation物件並對其設定一些屬性,然後將它與一個view建立關聯,最後這個view可以執行這個動畫。由此我們開始分析Animation的原始碼(基於Android 6.0):Animation及相關類的原始碼在frameworks/base/core/java/android/view/animation包中。其中Animation類是一個抽象類,雖然它沒有抽象方法,但是它有一個空方法protected void applyTransformation(float interpolatedTime, Transformation t)可以看到,它有兩個引數,第一個引數是 interpolatedTime 它代表插值後的時間,第二個引數是Transformation類的例項Transformation是一個實體類,它主要的內容是透明度和一個矩陣。所以子類實現了applyTransformation方法後可以針對插值時間來對Transformation做一定的操作來實現變化。同時可以看到applyTransformation是在Animtaion的getTransformation中呼叫的,public boolean getTransformation(long currentTime, Transformation outTransformation)其中currentTime作為一個引數,雖然期望的是當前時間,但是不是用還是由呼叫者決定。第二個引數目的是呼叫者傳進來作為收集變化資訊此方法除了呼叫回撥(開始,結束,重複),重點是呼叫applyTransformation前的這一句final float interpolatedTime = mInterpolator.getInterpolation
(normalizedTime);插值器的目的是為了將控制動畫速度的過程抽離出來,它是通過改變時間來改變最終效果。normalizedTime 的範圍是0.0f~1.0f但是interpolatedTime可以超出這個範圍所以在實現applyTransformation的時候需要考慮插值後的時間這個範圍之外的情況。下面以TranslateAnimation和AccelerateInterpolator來說明是如何實現的可以知道TranslateAnimation主要實現applyTransformation方法AccelerateInterpolator主要實現getInterpolation方法下面是兩個方法的原始碼:
  protected void applyTransformation(float interpolatedTime, Transformation t) {
  float dx = mFromXDelta;
  float dy = mFromYDelta;
  if (mFromXDelta != mToXDelta) {
                 dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
  }
  if (mFromYDelta != mToYDelta) {
                 dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
  }
  t.getMatrix().setTranslate(dx, dy);//改變了Transformation的矩陣偏移
  }
 
  public float getInterpolation(float input) {
  if (mFactor == 1.0f) {
             return input * input; //輸入0.5 返回0.25
  } else {
             return (float)Math.pow(input, mDoubleFactor);
  }
  }


如此一來,動畫的變化,就隱藏在Transformation之中了。既然已經知道了如何變化,現在就需要系統使用這種變化即應用到繪製中了。移步到View.java之中的draw(--,--,--)方法,其中applyLegacyAnimation方法是用來獲取變換的,其中有兩個部分需要注意 a.getTransformation(drawingTime, invalidationTransform, 1f);a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,  invalidationTransform);第一個就是獲取變換,第二個是獲取繪製無效區域。其實這個無效區域是繪製後的無效區域,因為很有可能需要下次繪製(保證動畫連續性)。而view在draw之中
  if (transformToApply != null) {
  if (concatMatrix) {
  if (drawingWithRenderNode) {
  renderNode.setAnimationMatrix(transformToApply.getMatrix());
  } else {
  // Undo the scroll translation, apply the transformation matrix,
  // then redo the scroll translate to get the correct result.
  canvas.translate(-transX, -transY);
  canvas.concat(transformToApply.getMatrix());
  canvas.translate(transX, transY);
  }
  parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
  }
 
  float transformAlpha = transformToApply.getAlpha();
  if (transformAlpha < 1) {
  alpha *= transformAlpha;
  parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
  }
  } 


分別從硬體加速和軟體繪製上 對canvas進行矩陣變換.除了TranslateAnimation,系統還有AlphaAnimation和ClipRectAnimation可供選擇.三、逐幀動畫原理(Frame Animation)原理:使用了Choreographer機制AnimationDrawable類是一個實現了逐幀動畫的類,可以看出,它只用來進行圖片的動態切換.AnimationDrawable類原始碼在frameworks/base/graphics/java/android/graphics/drawable/中public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable首先看到AnimationDrawable繼承了DrawableContainer,因為DrawableContainer是一個drawable的容器,可以儲存多個圖片同時,實現了Runnable介面,重寫了run方法根據原始碼中的start方法,它呼叫了setFrame方法,方法內部最重要的呼叫就是呼叫了 scheduleSelf(this, SystemClock.uptimeMillis() + mAnimationState.mDurations[frame]);上述方法實現是在Drawable類實現的
  public void scheduleSelf(Runnable what, long when) {
  final Callback callback = getCallback();
  if (callback != null) {
  callback.scheduleDrawable(this, what, when);
  }
  }


其中callback一般是drawable相關聯的view.可以看出,它接著回調了view的scheduleDrawable方法而這個方法最終會
 mAttachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed(
  Choreographer.CALLBACK_ANIMATION, what, who,
  Choreographer.subtractFrameDelay(delay));


來實現delay後,可以繪製下一幀的效果.因為AnimationDrawable所實現了runnable介面的run方法就是執行nextFrame.同時,AnimatedStateListDrawable和AnimatedVectorDrawable和AnimatedRotateDrawable(隱藏)都是具備一定的動畫效果其中,AnimatedStateListDrawable是在view狀態切換時可以實現兩個狀態直接的漸變如果想了解更多,需要對drawable有所瞭解.四、屬性動畫原理(Property Animation)原理:使用了Choreographer機制簡單的說,Chreographer是組織上層進行處理繪製的控制類,它會在每次vsync訊號來臨時,執行與繪製相關的過程.屬性動畫相關的方法在原始碼中所在的位置是frameworks/base/core/java/android/view/animation屬性動畫的基類是Animator.與Animation(補間動畫)不同,Animator的確定過程發生在繪製之前(甚至是佈局之前).因為ValueAnimator引入了Choreographer,Choreographer是Vsync訊號到來後進行view更新的控制類。它通過mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);這個動作來實現下次繪製之前,可以執行mAnimate任務來設定view的一些屬性來實現動畫。上述所說的view的一些屬性包括X,translateX,TranslateZ,ScaleX......等屬性對這些屬性的控制都會放到一個矩陣(Matrix),而這個矩陣放在RenderNode中需要注意的是,雖然RenderNode主要為硬體渲染服務,但是它儲存了一些屬性是軟體渲染也需要的,比如上面說的矩陣.通過硬體加速繪製時,因為矩陣已經在RenderNode中了,所以在draw方法中不用做特殊處理.而在軟體渲染中(draw方法),
  if (!childHasIdentityMatrix && !drawingWithRenderNode) {
  canvas.translate(-transX, -transY);
  canvas.concat(getMatrix());
  canvas.translate(transX, transY);
  }


其中,childHasIdentityMatrix 代表是不是單位矩陣drawingWithRenderNode 代表是不是開啟了硬體加速說明:觀察ValueAnimator的原始碼發現方法
  private void scheduleAnimation() {
  if (!mAnimationScheduled) {
  mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
  mAnimationScheduled = true;
  }
  }


以及mAnimate為
  // Called by the Choreographer.
  final Runnable mAnimate = new Runnable() {
  @Override
  public void run() {
  mAnimationScheduled = false;
  doAnimationFrame(mChoreographer.getFrameTime());
  }
  };


你如果對ValueAnimator添加了更新監聽(addUpdateListener)那麼你可以在每次更新的回撥(發生在上面的doAnimationFrame裡[實際是animateValue])比如進行view.setTranslateX(10),這種處理在隨後的繪製中就會有所體現參考文件: