Android動畫基礎——屬性動畫(Property Animation)

来源:http://www.cnblogs.com/fallblank/archive/2016/06/21/5604462.html
-Advertisement-
Play Games

本篇涉及例子下載:Github 本篇講android 3.0引入的屬性動畫框架,上篇寫視圖動畫View Animation時就說過ViewAnimation的缺點,那就是動畫作用的是view本身的視覺部分,view實際屬性並沒有隨著動畫的改變而變化。很多時候就需要額外去出來由於動畫引起的事件不同步, ...


本篇涉及例子下載:Github

本篇講android 3.0引入的屬性動畫框架,上篇寫視圖動畫View Animation時就說過ViewAnimation的缺點,那就是動畫作用的是view本身的視覺部分,view實際屬性並沒有隨著動畫的改變而變化。很多時候就需要額外去出來由於動畫引起的事件不同步,比如ViewAnimation已經講View移出了屏幕,但View的事件觸發還在原地,這就需要額外處理了。 但是,PropertyAnimation的引入就完全解決了這個問題,它可以保證動畫和事件的變化總是一起的發生的。其本質是View是屬性變化帶動View重繪來完成動畫。

目錄

  • 屬性動畫框架結構
  • 個人理解
  • TypeEvaluator與Interpolators
  • ObjectAnimator
  • ValueAnimator
  • 動畫集: Keyframes於AnimatorSet

一、屬性動畫框架結構

個人覺得瞭解繼承結構對學習框架幫助挺大的,所以開始先說一下屬性動畫的框架,我自己用StartUML畫了下麵的類圖: 結構類圖 畫得比較糙(StartUML用的不好, -_-!),上面沒畫出TypeEvaluator和Interpolator,一來是怕複雜了不好看重點信息,二來是後面會重點講。大致講一下各個類的作用:

  • Animator類:屬性動畫框架的基類,封裝了動畫時間、監聽器、動畫狀態的控制。
  • AnimatorListener:動畫狀態介面,封裝了不同狀態需要調用的方法,有如下方法
    • onAnimationStart() - 動畫開始時調用
    • onAnimationEnd() - 動畫結束時調用
    • onAnimationRepeat() - 動畫重覆時調用
    • onAnimationCancel() - 動畫取消時調用,註意這個方法調用後會接著調用onAnimationEnd()
  • AnimatorSet:類似於視圖動畫中的AnimationSet,它代表了一個動畫集,可以指定動畫集中各個動畫的順序,是實現複雜動畫必不可少的利器。
  • ValueAnimation:屬性動畫的核心類,它能驅動View的屬性變化進而實現動畫。
  • AnimationUpdateListenr:與ValueAnimation搭配使用,每次需要刷新動畫幀時會調用這個介面里的方法,進而對真正的View設置屬性完成動畫。它就包含一個介面方法:
    • onAnimationUpdate()
  • ObjectAnimator:ValueAnimator的便捷版本,也是平時使用最多的屬性動畫類,它通過反射調用自動完成View屬性的設置,非常簡單。
  • TimeAnimator:可是用於同步動畫的輔助類,可以在觸發動畫幀時通過介面讓動畫執行指定的時間點。

以上便是屬性動畫框架的大致結構,關於ypeEvaluator和Interpolator後面有講到。

二、個人理解

1.屬性動畫時“真正的”動畫機制嗎? 我覺得不是(放下手頭的磚,讓我說完 ㄟ( ▔, ▔ )ㄏ),為什麼我說不是?它並不是直接生成一段動畫,它是通過在眾多動畫幀上改變View的屬性,然後View重繪,進而達到動畫的目的。或者把它理解成一個過渡值產生器更合適,它在每個動畫幀時間點上產生一個過渡值,然後把這個值設為相應的屬性。 2.屬性動畫VS視圖動畫: 屬性動畫相比於視圖動畫的優點是顯而易見的,因為動畫的來源是由於View的屬性變化引起的重繪,所以不存在事件與動畫不一致的情況。 視圖動畫最大的優點就是它簡單了,不需要添加各種監聽器就能實現動畫。 我還沒有兩種動畫性能的對比,不知道在實現相同效果時孰優孰劣,所以不敢妄下結論。留到後面討論吧! 3.ValueAnimator VS ObjectAnimator ObjectAnimator是ValueAnimator的便捷類,為了避免複雜的實現儘量選擇用ObjectAnimator。

三、TypeEvaluator與Interpolators

前面多次說道動畫幀(Frame),什麼叫Frame呢?相當於視圖動畫中的幀動畫,也就是每一個單獨的頁面,類似於膠卷電影中的一格。人眼的可察覺刷新頻率是24幀每秒,在低於這個頻率的動畫中我們看到影像就不是連續的。而熟悉動畫中用來指定動畫刷新頻率的是setFrameDelay()方法,預設情況下它的值是10ms,也就是100幀每秒。有了刷新頻率,如何講它映射到值呢?這就要用到TypeEvaluator和Interpolators了! 先說Interpolators(插值器):它的總用是怎樣將一個時間分數裝換為一個值分數,兩者的相對關係體現了動畫的形式:勻速、加速、先加速後減速,或者其它的數學變換。怎麼理解講一個時間分數裝換為值分數呢?來看一下介面吧!所以插值器都是TimeInterpolator介面的子類,其中聲明的方法:

abstract float  getInterpolation(float input)

input是一個已經過去的時間占總時間的比例,也就是動畫已經完成的百分比。返回的也是個小數,代表了希望這個點完成的總動畫的百分數。假設有這樣一個插值器,它接受0.5,返回0.8。這意味著這個差值器在經過50%的時間時,希望動畫完成了80%。為什麼是希望呢?因為真正映射為屬性值是由TypeEvaluator來完成的。 TypeEvaluator:(翻譯成類型求值器吧!這樣比較貼合它的意思)它完成了差值器返回的值到實際值的映射?或許你會疑問為什麼需要這個值,不應該是下麵這個公式嗎?

startVal:起始值 endVal:結束值 percent:完成百分比 progress:當前值 progress = startVal + (endVal-startVal)*progress

對,的確是這樣的。內置的IntEvator、floatEvator也是這樣的,但是我們動畫的類型不一定都是這種數值類型的值吧!我們還可以動畫字元串,在不同的時間點顯示不同的字串,這時上述公式就不成立了。所以TypeEvaluator就是為了達到自定義裝換的需求的。要實現滿足自己需求的TypeEvaluator也很簡單,只需要實現TypeEvaluator介面就可以了,它聲明的方法如:

abstract T  evaluate(float fraction, T startValue, T endValue)

T是要動畫的屬性類型,fraction是值分數,startValue、endValue是進行動畫屬性的起始值和終止值。返回映射的中間值。 到這,可以對屬性動畫的工作原理做個簡單的接受了。 動畫需要指定持續時間,屬性起始值,屬性終止值,幀延時。然後每過幀延時的時間間隔,觸發插值器,接著觸發值裝換器,接下來是觸發AnimationUpdateListenr中的onAnimationUpdate()方法重新設置View的屬性,接著View重繪,如此迴圈知道動畫結束。為了方便,我畫瞭如下流程圖:
PropertyAnimationInvoke
ps:這符圖不是很嚴謹,因為有些內容是不好表達,比如動畫幀的計時是連續的,並不是等到上一幀完成了才開始計數下一幀。其次就是Animator本身的Listener是班法畫上去的,當Animator調用cance(),end()之類的方法是能打斷這個流程的。但是這圖對理解過程還是不錯的。

四、ObjectAnimator

先來講最常使用的屬性動畫,ObejectAnimator,通過置頂好動畫作用對象個對用屬性等設置值後,ObjectAnimator能夠通過反射區設置對象屬性達到動畫的目的。 ObjectAnimator提供了四種靜態方法來構造自己的對象,分別是:ofInt、ofFloat、ofArgb、ofObject 。它們動畫的值類型正如名字中說的那樣,如ofInt動畫作用於一個int域,offArgb註意與一個帶透明的顏色域。 來看一個用動畫改變欄背景的例子

public class MainActivity extends AppCompatActivity {

    @Bind(R.id.btn_bg) Button mGradientBg;
    ObjectAnimator mAnimator;

    @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final View view = findViewById(android.R.id.content);
        final Drawable bgd =view.getBackground();
        mAnimator = ObjectAnimator.ofArgb(view,"backgroundColor",0x881DDA38,0x88D48AB2);
        mAnimator.setDuration(5*1000);
        mAnimator.setRepeatMode(ObjectAnimator.REVERSE);
        mAnimator.addListener(new AnimatorListenerAdapter() {
            @Override public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                view.setBackground(bgd);
            }
        });
        ButterKnife.bind(this);
    }


    @OnClick(R.id.btn_bg) void onGradientActionbarClicked() {
        mAnimator.start();
    }
}

效果如下:
ObjectAnimator
上面就是ObjectAnimation使用過程,先通過四個靜態方法構造出一個ObjectAnimation對象,這裡使用的是ofArgb:

static ObjectAnimator   ofArgb(Object target, String propertyName, int... values)
  • 第一個是作用的對象
  • 第二個是string指定的屬性名
  • 第三個可變參數是動畫的起始值(可省略,省略以當前值算,且必須有getter)、終止值。

ObjectAnimator作用的域在對象是必須有相應的setter,getter(用於省略初始值的情況)方法,否則反射調用不了會報錯。至於沒有setter方法的域如何動畫,官方Guide提供瞭如下解決方案:

  • 使用包裝類,加入setter,getter。
  • 添加相應方法(自己定義View適合)
  • 換用ValueAnimator。

講一下使用包裝類,假設有類A,且A類中有域a,我可以正常操作A.a(訪問和寫值),現在我要動畫A的a域,直接用ObjectAnimator是不行了,因為A類中沒有A.setA()這個方法,那該怎麼辦呢?使用包裝類,寫一個A的包裝類AWrapper,它接受一個A對象來構造自己,然後裡面有一個setter和getter方法來設置和讀取A的a域。現在就可以在AWrapper上使用ObjectAnimator了。

六、ValueAnimator

ValueAnimator提供的便利性大大強於ObejectAnimator,因為需要你自己去更新對象的值,ValueAnimator只是為了提供了一個過渡值。為了更新對象的值,你就必須去設置前面講的AnimationUpdateListenr介面。一個簡單的例子來完成ValueAnimator的使用:

protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       ButterKnife.bind(this);
       final float X = 0, Y = 500;
       LogUtil.d("X,Y = "+X+","+Y);
       mValueAnimator = ValueAnimator.ofFloat(0f, 1f);
       mValueAnimator.setDuration(3 * 1000);
       mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
           @Override public void onAnimationUpdate(ValueAnimator animation) {
               mChangelocation.setY(Y * (Float)animation.getAnimatedValue());
               mChangelocation.invalidate();
           }
       });
       mValueAnimator.addListener(new AnimatorListenerAdapter() {
           @Override public void onAnimationEnd(Animator animation) {
               super.onAnimationEnd(animation);
               mChangelocation.setY(Y);
           }
       });
  }
  @OnClick(R.id.btn_cl) void onChangeLocationClicked() {
        mValueAnimator.start();
    }
}

效果:
ObjectAnimator

七、動畫集: Keyframes於AnimatorSet

類似於ViewAnimation中的動畫集,參考資料:AnimatorSet
Keyframes實現動畫集:Keyframes

本篇涉及例子下載:Github


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 經常我們看電影都會看到電影結束後會出現一段介紹這個電影的導演、編劇、主演、友情出演等等一些電影信息的滾動字幕,那麼那個效果可以用html+css+div實現。具體實現代碼如下: html代碼: 滾動標簽:<marquee>要滾動的文字/圖像<img src=””> </marquee> 下麵是我對滾 ...
  • 1,給第一人稱控制器添加腳本:playercollisions.js 腳本中只定義變數,先不添加方法: #pragma strict var door_open_time:float=3.0; var door_open_sound:AudioClip; var door_shut_sound:Au ...
  • 最近在做JS演算法項目時發現一個令我匪夷所思的問題, 這裡想記錄一下問題。 首先介紹一下字元串replace()方法的基本用法。 replace() 方法使用一個替換值(replacement)替換掉一個匹配模式(pattern)在原字元串中某些或所有的匹配項,並返回替換後的字元串。這個替換模式可以是 ...
  • App Store: Pinyin Comparison 拼音辨別 本人不懂韓文和日文,靠的是谷歌翻譯,希望不要被噴 1. 支持韓文和日文 2. 豐富字詞庫 1. 지원 한국과 일본 2. 풍부한 단어의 동의어 사전 1.サポート韓國と日本 2.豊富な単語シソーラス App Store: Piny ...
  • 一,效果圖。 二,工程圖。 三,代碼。 RootViewController.h #import <UIKit/UIKit.h> @interface RootViewController : UIViewController <UITextViewDelegate> { UITextView *p ...
  • 通常情況下,只有高端用戶才會經常用到終端應用。這並不意味著命令行非常難學,有的時候命令行可以輕鬆、快速的解決問題。相信所有Mac用戶都嘗試過命令行,今天為大家帶來9個非常實用的命令行操作。一些命令行需要安裝Xcode之後才可以實用,Xcode在Mac App Store中免費供應。1.使用caffe ...
  • 1.Android控制項架構下圖是UI界面架構圖,每個Activity都有一個Window對象,通常是由PhoneWindow類來實現的。PhoneWindow將DecorView作為整個應用視窗的根View,DecorView將屏幕分成兩部分:TitleView和ContentView。Conten ...
  • 之前學習swift時的個人筆記,根據github: "the swift programming language in chinese" 學習、總結,將重要的內容提取,加以理解後整理為學習筆記,方便以後查詢用。詳細可以參考 "the swift programming language in ch ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...