Android动画原理
生活随笔
收集整理的這篇文章主要介紹了
Android动画原理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、前言 Android動畫包含三種:補間動畫(Tween Animation),幀動畫(Frame Animation),屬性動畫 (Property Animation)。其中屬性動畫是從Android 3.0之后加入的。 本文著重介紹三種動畫的實現原理,閱讀本文的前提是應該可以簡單的使用上述三種動畫。 如不熟悉可以參考以下鏈接, 補間動畫:http://www.cnblogs.com/whoislcj/p/5730520.html 逐幀動畫:http://www.cnblogs.com/whoislcj/p/5733740.html 屬性動畫:http://www.cnblogs.com/whoislcj/p/5738478.html 二、補間動畫原理(Tween Animation) 原理:在繪制的過程中,嘗試獲取動畫在當前時刻的變換,然后應用到view的繪制中。 ? 說明: 繪制是顯示view所必不可少的過程,通過view的draw方法可以看到繪制的流程。 查看View.java的源碼,可以發現有兩個draw方法,一個有一個參數,一個有三個參數。分別是 public void draw(Canvas canvas) boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) 其中一個參數的draw方法是具體的繪制過程(繪制背景、繪制內容、繪制前景...) 含有三個參數的draw方法是由父布局調用的(ViewGroup.drawChild),因為Android繪制的過程是從根布局開始, 子View是否能夠繪制由父布局決定。這個方法就是提前處理繪制的位置的地方,補間動畫也是在此處進行了處理以實現動畫的效果。 先舉一個例子, Animation?translateAnimation?=?new?TranslateAnimation(0,?100,?0,?0); translateAnimation.setDuration(500); translateAnimation.setInterpolator(new AccelerateInterpolator()); translateAnimation.setFillAfter(true);//設置動畫結束后保持當前的位置(即不返回到動畫開始前的位置) imageView.startAnimation(translateAnimation); 一個動畫重要的東西就四個部分,開始時間,結束時間,做什么,如何做。 上述是一個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),這種處理在隨后的繪制中就會有所體現 參考文檔: http://www.cnblogs.com/whoislcj/p/5730520.html https://en.wikipedia.org/wiki/Identity_matrix http://zuiniuwang.blog.51cto.com/3709988/721798/ http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/1214/6858.html
轉載于:https://www.cnblogs.com/vete-l/p/7063285.html
總結
以上是生活随笔為你收集整理的Android动画原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小试牛刀JavaScript鼠标事件
- 下一篇: 配置ntp时间服务器