04.Android之動畫問題

楊充發表於2019-01-11

目錄介紹

  • 4.0.0.1 Android中有哪幾種型別的動畫,屬性動畫和補間動畫有何區別?補間動畫和屬性動畫常用的有哪些?
  • 4.0.0.2 View動畫為何不能真正改變View的位置?而屬性動畫為何可以?屬性動畫是如何改變View的屬性?
  • 4.0.0.3 補間動畫是如何作用於view的,從原始碼角度分析以下?為何說補間動畫沒有改變View的屬性?
  • 4.0.0.6 屬性動畫插值器和估值器的作用?插值器和估值器分別是如何更改動畫的?
  • 4.0.0.7 使用動畫會出現哪些問題?動畫佔用大量記憶體,如何優化?使用動畫的注意事項有哪些?

好訊息

  • 部落格筆記大彙總【15年10月到至今】,包括Java基礎及深入知識點,Android技術部落格,Python學習筆記等等,還包括平時開發中遇到的bug彙總,當然也在工作之餘收集了大量的面試題,長期更新維護並且修正,持續完善……開源的檔案是markdown格式的!同時也開源了生活部落格,從12年起,積累共計500篇[近100萬字],將會陸續發表到網上,轉載請註明出處,謝謝!
  • 連結地址:github.com/yangchong21…
  • 如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起於忽微,量變引起質變!所有的筆記將會更新到GitHub上,同時保持更新,歡迎同行提出或者push不同的看法或者筆記!

4.0.0.1 Android中有哪幾種型別的動畫,屬性動畫和補間動畫有何區別?

  • 常見三類動畫
    • View動畫(View Animation)/補間動畫(Tween animation):對View進行平移、縮放、旋轉和透明度變化的動畫,不能真正的改變view的位置。應用如佈局動畫、Activity切換動畫
    • 逐幀動畫(Drawable Animation):是View動畫的一種,它會按照順序播放一組預先定義好的圖片
    • 屬性動畫(Property Animation):對該類物件進行動畫操作,真正改變了物件的屬性
  • 屬性動畫和補間動畫區別
    • 屬性動畫才是真正的實現了view的移動,補間動畫對view的移動更像是在不同地方繪製了一個影子,實際物件還是處於原來的地方。當動畫的repeatCount設定為無限迴圈時,如果在Activity退出時沒有及時將動畫停止,屬性動畫會導致Activity 無法釋放而導致記憶體洩漏,而補間動畫卻沒問題。xml檔案實現的補間動畫,複用率極高。在 Activity切換,視窗彈出時等情景中有著很好的效果。
    • 補間動畫還有一個致命的缺陷,就是它只是改變了View的顯示效果而已,而不會真正去改變View的屬性。什麼意思呢?比如說,現在螢幕的左上角有一個按鈕,然後我們通過補間動畫將它移動到了螢幕的右下角,現在你可以去嘗試點選一下這個按鈕,點選事件是絕對不會觸發的,因為實際上這個按鈕還是停留在螢幕的左上角,只不過補間動畫將這個按鈕繪製到了螢幕的右下角而已。下面這張圖摘自網路!
    • image
  • 補間動畫和幀動畫xml檔案存放的位置
    • 補間動畫是放置到res/anim/下面
    • 幀動畫是放置到res/drawable/下面,子節點為animation-list,在這裡定義要顯示的圖片和每張圖片的顯示時長
  • 補間動畫和屬性動畫常用的有哪些?技術部落格大總結
    • View動畫框架是舊的框架,只能用於Views。比較容易設定和能滿足許多應用程式的需要。View動畫框架中一共提供了AlphaAnimation(透明度動畫)、RotateAnimation(旋轉動畫)、ScaleAnimation(縮放動畫)、TranslateAnimation(平移動畫)四種型別的補間動畫;並且View動畫框架還提供了動畫集合類(AnimationSet),通過動畫集合類(AnimationSet)可以將多個補間動畫以組合的形式顯示出來。
    • 與屬性動畫相比View動畫存在一個缺陷,View動畫改變的只是View的顯示,而沒有改變View的響應區域,並且View動畫只能對View做四種型別的補間動畫。因此Google在Android3.0(API級別11)及其後續版本中新增了屬性動畫框架,從名稱中就可以知道只要某個類具有屬性(即該類含有某個欄位的set和get方法),那麼屬性動畫框架就可以對該類的物件進行動畫操作(其實就是通過反射技術來獲取和執行屬性的get,set方法),同樣屬性動畫框架還提供了動畫集合類(AnimatorSet),通過動畫集合類(AnimatorSet)可以將多個屬性動畫以組合的形式顯示出來。

4.0.0.2 View動畫為何不能真正改變View的位置?而屬性動畫為何可以?屬性動畫是如何改變View的屬性?

  • View動畫為何不能真正改變View的位置?而屬性動畫為何可以?
    • View動畫改變的只是View的顯示,而沒有改變View的響應區域;而屬性動畫會通過反射技術來獲取和執行屬性的get、set方法,從而改變了物件位置的屬性值。
    • Animation產生的動畫資料實際並不是應用在View本身的,而是應用在RenderNode或者Canvas上的,這就是為什麼Animation不會改變View的屬性的根本所在。
  • 屬性動畫是如何改變View的屬性?

4.0.0.3 補間動畫是如何作用於view的,從原始碼角度分析以下?為何說補間動畫沒有改變View的屬性?

  • 關於補間動畫原理
    • 要了解Android動畫是如何載入出來的,我們首先要了解Android View 是如何組織在一起的.每個視窗是一顆View樹. RootView是DecorView,在佈局檔案中宣告的佈局都是DecorView的子View.是通過setContentView來設定進入視窗內容的. 因為View的佈局就是一棵樹.所以繪製的時候也是按照樹形結構來遍歷每個View進行繪製.ViewRoot.java中 draw函式準備好Canvas後 呼叫 mView.draw(canvas),這裡的mView是DecorView.
    • 下面看一下遞迴繪製的幾個步驟:技術部落格大總結
    • 1.繪製背景
    • 2.如果需要,儲存畫布(canvas),為淡入淡出做準備
    • 3.通過呼叫View.onDraw(canvas)繪製View本身的內容
    • 4.通過 dispatchDraw(canvas)繪製自己的孩子,dispatchDraw->drawChild->child.draw(canvas) 這樣的呼叫過程被用來保證每個子 View 的 draw 函式都被呼叫
    • 5.如果需要,繪製淡入淡出相關的內容並恢復儲存的畫布所在的層(layer)
    • 6.繪製修飾的內容(例如滾動條)
    • 當一個 ChildView 要重畫時,它會呼叫其成員函式 invalidate() 函式將通知其 ParentView 這個 ChildView 要重畫,這個過程一直向上遍歷到 ViewRoot,當 ViewRoot 收到這個通知後就會呼叫上面提到的 ViewRoot 中的 draw 函式從而完成繪製。Android 動畫就是通過 ParentView 來不斷調整 ChildView 的畫布座標系來實現的
  • 如何計算補間動畫資料
    • 首先進入Animation類,然後找到getTransformation方法,主要是分析這個方法邏輯,如圖所示
      • image
    • 那麼這個方法中做了什麼呢?Animation在其getTransformation函式被呼叫時會計算一幀動畫資料,而上面這些屬性基本都是在計算動畫資料時有相關的作用。
    • 第一步:若startTime為START_ON_FIRST_FRAME(值為-1)時,將startTime設定為curTime
    • 第二步:計算當前動畫進度:
      • normalizedTime = (curTime - (startTime + startOffset))/duration
      • 若mFillEnabled==false:將normalisedTime夾逼至[0.0f, 1.0f]
    • 第三步:判斷是否需要計算動畫資料:
      • 若normalisedTime在[0.0f, 1.0f],需計算動畫資料
      • 若normalisedTime不在[0.0f, 1.0f]:
        • normalisedTime<0.0f, 僅當mFillBefore==true時才計算動畫資料
        • normalisedTime>1.0f, 僅當mFillAfter==true時才計算動畫資料
    • 第四步:若需需要計算動畫資料:
      • 若當前為第一幀動畫,觸發mListener.onAnimationStart
      • 若mFillEnabled==false:將normalisedTime夾逼至[0.0f, 1.0f]
      • 根據插間器mInterpolator調整動畫進度:
      • interpolatedTime = mInterpolator.getInterpolation(normalizedTime)
      • 若動畫反轉標誌位mCycleFlip為true,則
      • interpolatedTime = 1.0 - normalizedTime
      • 呼叫動畫更新函式applyTransformation(interpolatedTime, transformation)計算出動畫資料
    • 第五步:若夾逼之前normalisedTime大於1.0f, 則判斷是否需繼續執行動畫:
      • 已執行次數mRepeatCount等於需執行次數mRepeated
        • 若未觸發mListener.onAnimationEnd,則觸發之
      • 已執行次數mRepeatCount不等於需執行次數mRepeated技術部落格大總結
        • 自增mRepeatCount
        • 重置mStartTime為-1
        • 若mRepeatMode為REVERSE,則取反mCycleFlip
        • 觸發mListener.onAnimationRepeat

4.0.0.6 屬性動畫插值器和估值器的作用?插值器和估值器分別是如何更改動畫的?

  • 插值器(Interpolator):根據時間流逝的百分比計算出當前屬性值改變的百分比。確定了動畫效果變化的模式,如勻速變化、加速變化等等。View動畫和屬性動畫均可使用。常用的系統內建插值器:
    • 線性插值器(LinearInterpolator):勻速動畫
    • 加速減速插值器(AccelerateDecelerateInterpolator):動畫兩頭慢中間快
    • 減速插值器(DecelerateInterpolator):動畫越來越慢
  • 型別估值器(TypeEvaluator):根據當前屬性改變的百分比計算出改變後的屬性值。針對於屬性動畫,View動畫不需要型別估值器。常用的系統內建的估值器:技術部落格大總結
    • 整形估值器(IntEvaluator)
    • 浮點型估值器(FloatEvaluator)
    • Color屬性估值器(ArgbEvaluator)

4.0.0.7 使用動畫會出現哪些問題?動畫佔用大量記憶體,如何優化?使用動畫的注意事項有哪些?

  • 使用動畫會出現哪些問題?
    • OOM問題:這個問題主要出現在幀動畫中,當圖片數量較多且圖片較大時就極易出現OOM,這個在實際開發中要尤其注意,儘量避免使用幀動畫。
    • 記憶體洩露:在屬性動畫中有一類無限迴圈的動畫,這類動畫需要在Activity退出時及時停止,否則將導致Activity無法釋放從而造成記憶體洩露,通過驗證後發現View動畫並不存在此問題。
  • 動畫佔用大量記憶體,如何優化?
  • 使用動畫的注意事項有哪些?
    • OOM問題:這個問題主要出現在幀動畫中,當圖片數量較多且圖片較大時就極易出現OOM,這個在實際開發中要尤其注意,儘量避免使用幀動畫。
    • 記憶體洩露:在屬性動畫中有一類無限迴圈的動畫,這類動畫需要在Activity退出時及時停止,否則將導致Activity無法釋放從而造成記憶體洩露,通過驗證後發現View動畫並不存在此問題。
    • 相容性問題:動畫在3.0以下的系統有相容性問題,在某些特殊場景可能無法正常工作,因此要做好適配工作。
    • View動畫的問題:View動畫是對View的影像做動畫,並不是真正改變View的狀態,因此有時候會出現動畫完成後View無法隱藏的現象,即setVisibility(View.GOEN)失效了,這個時候只要呼叫view.clearAnimation()清除View動畫即可解決問題。技術部落格大總結
    • 不要使用px:在進行動畫的過程中,要儘量使用dp,使用px會導致在不用的裝置上有不用的效果。
    • 動畫元素的互動:從3.0開始,將view移動(平移)後,屬性動畫的單擊事件觸發位置為移動後的位置,但是View動畫仍然在原位置。在Android3.0以前的系統中,不管是View動畫還是屬性動畫,新位置都無法觸發單擊事件同時,老位置仍然能觸發單擊事件(因為屬性動畫在Android3.0以前是沒有的,是通過相容包實現的,底層也是呼叫View動畫)。
    • 硬體加速:使用動畫的過程中,建議開啟硬體加速,這樣會提高動畫的流暢性。
    • 開啟方法:
    • 在你的Android manifest檔案,新增hardwareAccelerated屬性就可以了。可以給整個application新增,也可以單獨給一個acitivty新增,該屬性預設值為false;

關於其他內容介紹

01.關於部落格彙總連結

02.關於我的部落格

相關文章