1. 程式人生 > >Android Transition動畫

Android Transition動畫

動畫在app中的重要性我不用多說了,在應用中使用一些符合系統設計規範的動畫可以顯著提升使用者體驗和自身逼格,我們也接觸過很多種動畫,幀動畫和屬性動畫大家應該很熟悉了,用的也比較多,今天我們要說的是Transition動畫,這個Api是在API 19(Android 4.4)中加入的。

為什麼要引入Transition?

由於在Android引入了Metrial Desigon之後,動畫的場面越來越大,比如以前我們製作一個動畫可能涉及到的View就一個,或者就那麼幾個,如果我們一個動畫中涉及到了當前Activity檢視樹中的各個View,那麼情況就複雜了。比如我們要一次針對檢視樹中的10個View進行動畫,這些View的效果都不同,可能有的是平移,有的是旋轉,有的是淡入淡出,那麼不管是使用之前哪種方式的動畫,我們都需要為每個View定義一個開始狀態和結束狀態【關鍵幀,比如放縮,我們得設定fromXScale和toXScale 】,隨著View個數的增加,這個情況會越來越複雜。Transition主要做的事情是:

  1. 捕獲每一個 View 的起始和結束狀態
  2. 根據這些資料來建立從一個場景到另一個場景間的過渡動畫。

Transition 例子

考慮這樣一個例子,當用戶點選螢幕,讓activity中的view逐漸消失。使用安卓的transition框架,我們只需幾行程式碼就可完成,如下:

public class ExampleActivity extends Activity implements View.OnClickListener {
    private ViewGroup mRootView;
    private View mRedBox, mGreenBox, mBlueBox, mBlackBox;
    @Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRootView = (ViewGroup) findViewById(R.id.layout_root_view); mRootView.setOnClickListener(this); mRedBox = findViewById(R.id.red_box); mGreenBox = findViewById(R.id.green_box); mBlueBox = findViewById(R.id.blue_box); mBlackBox = findViewById(R.id.black_box); } @Override
public void onClick(View v) { TransitionManager.beginDelayedTransition(mRootView, new Fade()); toggleVisibility(mRedBox, mGreenBox, mBlueBox, mBlackBox); } private static void toggleVisibility(View... views) { for (View view : views) { boolean isVisible = view.getVisibility() == View.VISIBLE; view.setVisibility(isVisible ? View.INVISIBLE : View.VISIBLE); } } }

為了更好的理解幕後發生的事情,讓我們來一步一步的分析,假設最開始每個view都是可見的:

  1. 當點選事件發生之後呼叫TransitionManager的beginDelayedTransition()方法,並且傳遞了mRootView和一個Fade物件最為引數。之後,framework會立即呼叫transition類的captureStartValues()方法為每個view儲存其當前的可見狀態(visibility)。

  2. 當beginDelayedTransition返回之後,在上面的程式碼中將每個view設定為不可見。

  3. 在接下來的顯示中framework會呼叫transition類的captureEndValues()方法,記錄每個view最新的可見狀態。

  4. 接著,framework呼叫transition的createAnimator()方法。transition會分析每個view的開始和結束時的資料發現view在開始時是可見的,結束時是不可見的。Fade(transition的子類)會利用這些資訊建立一個用於把view的alpha屬性變為0的AnimatorSet,並且將此AnimatorSet物件返回。

注意: 讀者可以在這裡回想假如不使用transition框架,我們自己使用屬性動畫(Animator)來實現是不是複雜很多,其實transition框架的作用就是封裝了屬性動畫的操作。

這個簡單的例子強調了transition框架的兩個主要優點。第一、Transitions抽象和封裝了屬性動畫,Animator的概念對開發者來說是透明的,因此它極大的精簡了程式碼量。開發者所做的所有事情只是改變一下view前後的狀態資料,Transition就會自動的根據狀態的區別去生成動畫效果。第二、不同場景之間變換的動畫效果可以簡單的通過使用不同的Transition類來改變,本例中用的是Fade。

這裡寫圖片描述

實現上圖中的那兩個不同的動畫效果可以將Fade替換成Slide或者Explode即可。在接下來的文章中你將會發現,這些優點將使得我們只用少量程式碼就可以建立複雜的Activity 和Fragment切換動畫。在接下來的小節中,將看到是如何使用Lollipop的Activity 和Fragment transition API來實現這種變換的。

5.0中的Activity和Fragment Transition

Android 5.0中Transition可以被用來實現Activity或者Fragment切換時的異常複雜的動畫效果。雖然在以前的版本中,已經可以使用Activity的overridePendingTransition() 和 FragmentTransaction的setCustomAnimation()來實現Activity或者Fragment的動畫切換,但是他們僅僅侷限與將整個檢視一起動畫變換。新的Lollipop api更進了一步,讓單獨的view也可以在進入或者退出其佈局容器中時發生動畫效果,甚至還可以在不同的activity/Fragment中共享一個view。

在開始講解之前我們先做一些約定,雖然下面的約定是針對activity的,但是在Fragment中也是一樣的約定。

A和B分別是兩個Activity,假設activity A 呼叫activity B。將A代表呼叫Activity ,B代表被呼叫Activity。

Activity transition API圍繞退出(exit),進入(enter),返回(return)和再次進入(reenter)四種transition。按照上面對A和B的約定,我這樣描述這一過程。

Activity A的退出變換(exit transition)決定了在A呼叫B的時候,A中的View是如何播放動畫的。

Activity B的進入變換(enter transition)決定了在A呼叫B的時候,B中的View是如何播放動畫的。

Activity B的返回變換(return transition)決定了在B返回A的時候,B中的View是如何播放動畫的。

Activity A的再次進入變換(reenter transition)決定了在B返回A的時候,A中的View是如何播放動畫的。

最後framework提供了兩種Activity transition- 內容transition和共享元素的transition:

A content transition determines how an activity’s non-shared views—called transitioning views—enter or exit the activity scene.

A shared element transition determines how an activity’s shared elements (also called hero views) are animated between two activities.


上圖中演示了Google Play Newsstand 應用的效果,雖然我們無法檢視它的原始碼,但是我敢打賭它用了以下的transition:

activity A 中exit和reenter transition是為null的,因為A中的非共享view在退出和再次進入的時候沒有動畫效果。
activity B中的enter content transition使用了自定義的slide-in變換。該變換使B中list的元素從下到上過度。
activity B中return content transition是一組TransitionSet,同時播放了兩個子元素的變換:上半部分和下半部分的slide變換。看起來就像整個介面被從從中間分割成了兩半。
enter and return 共享元素變換是用了ChangeImageTransform。讓兩個activity中的ImageView無縫切換。

Activity Transition API介紹

用5.0的API建立一個基本的Activity transition是較為簡單的。下面的總結是實現Activity transition的步驟。這篇文章主要是對Activity transition做簡單的介紹,作為引入的篇章。在後續的文章中我們再介紹一些高階的用法。

  • 在呼叫與被呼叫的activity中,通過設定Window.FEATURE_ACTIVITY_TRANSITIONS 和 Window.FEATURE_CONTENT_TRANSITIONS 來啟用transition api ,可以通過程式碼也可以通過設定主題來啟用:

程式碼方式,在setContentView之前呼叫:

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

主題xml:

<item name="android:windowContentTransitions">true</item>
  • 分別在呼叫與被呼叫的activity中設定exit 和enter transition。Material主題預設會將exit的transition設定成null而enter的transition設定成Fade .如果reenter 或者 return transition沒有明確設定,則將用exit 和enter的transition替代。

  • 分別在呼叫與被呼叫的activity中設定exit 和enter 共享元素的transition。Material主題預設會將exit的共享元素transition設定成null而enter的共享元素transition設定成@android:transition/move.如果reenter 或者 return transition沒有明確設定,則將用exit 和enter的共享元素transition替代。

    開始一個activity的content transaction需要呼叫startActivity(Context, Bundle)方法,將下面的bundle作為第二個引數:


ActivityOptions.makeSceneTransitionAnimation(activity, pairs).toBundle();

其中pairs引數是一個數組:Pair

Fragment Transition API介紹

如果你想在Fragment中使用transition,除了一小部分區別之外和activity大體一致:

  1. Content的exit, enter, reenter, 和return transition需要呼叫fragment的相應方法來設定,或者通過fragment的xml屬性來設定。

  2. 共享元素的enter和return transition也n需要呼叫fragment的相應方法來設定,或者通過fragment的xml屬性來設定。

  3. 雖然在activity中transition是被startActivity()和finishAfterTransition()觸發的,但是Fragment的transition卻是在其被FragmentTransaction執行下列動作的時候自動發生的。added, removed, attached, detached, shown, ,hidden。

  4. 在Fragment commit之前,共享元素需要通過呼叫addSharedElement(View, String) 方法來成為FragmentTransaction的一部分。