Android Transition(Android過渡動畫)

tuacy發表於2017-12-20

參考連結:
- http://www.jianshu.com/p/0af52be90ae6
- http://www.jianshu.com/p/e497123652b5

       在Android 4.4 Transition 就已經引入了,但在Android 5.0(API 21)之後,Transition 被更多的應用起來。相對於View Animation或Property Animator,Transition動畫更加具有特殊性,Transition可以看作對Property Animator的高度封裝。不同於Animator,Transition動畫具有視覺連續性的場景切換。

       為了對Transition有一個大概的瞭解,我們通過:Scene Transition(場景過渡動畫)、Activity過渡動畫、Shared Element Transition(共享元素過渡動畫)這三個方面來做一個簡單的瞭解

一、Scene Transition(場景過渡動畫)

       場景過渡動畫是指以動畫的形式實現View兩個場景的切換(從一個場景切換到另一個場景)。而且在切換過程中通過Transition來設定不同的過渡動畫效果。

       場景過渡動畫中有兩個特別關鍵概念:Scene(場景),Transition(過渡)

  • Scene:Scene代表一個場景。Scene儲存了一個檢視層級結構,包括它所有的views以及views的狀態,通常由getSceneForLayout (ViewGroup sceneRoot,int layoutId,Context context)獲取Scene例項。Transition框架可以實現在starting scene和ending scene之間執行動畫。而且大多數情況下,我們不需要建立starting scene,因為starting scene通常由當前UI狀態決定,我們只需要建立ending scene。
  • Transition:Transiton則是用來設定過渡動畫效果用的。而且系統給提供了一些非常使用的Transtion動畫效果,如下表所示:
系統Transition 解釋
ChangeBounds 檢測View的位置邊界建立移動和縮放動畫(關注佈局邊界的變化)
ChangeTransform 檢測View的scale和rotation建立縮放和旋轉動畫(關注scale和ratation的變化)
ChangeClipBounds 檢測View的剪下區域的位置邊界,和ChangeBounds類似。不過ChangeBounds針對的是view而ChangeClipBounds針對的是view的剪下區域setClipBound(Rect rect) 中的rect(關注的是setClipBounds(Rect rect)rect的變化)
ChangeImageTransform 檢測ImageView的ScaleType,並建立相應動畫(關注的是ImageView的scaleType)
Fade 根據View的visibility狀態的的不同建立淡入淡動畫,調整的是透明度(關注的是View的visibility的狀態)
Slide 根據View的visibility狀態的的不同建立滑動動畫(關注的是View的visibility的狀態)
Explode 根據View的visibility狀態的的不同建立分解動畫(關注的是View的visibility的狀態)
AutoTransition 預設動畫,ChangeBounds、Fade動畫的集合

       要想實現一個場景過渡動畫,至少需要一個transition例項和一個ending scene例項。並通過TransitionManager執行過渡動畫。TransitionManager執行動畫有兩種方式:TransitionManager.go()、beginDelayedTransition()。下面簡單來介紹下這兩種開啟場景動畫的方式。

1.1、TransitionManager.go()開啟場景動畫

用說TransitionManager.go()實現場景動畫之前,先上效果圖


TransitionManager.go()實現場景動畫

       TransitionManager.go()需要兩個引數:第一個引數代表要過渡到的場景(end scene)、第二個引數過渡動畫(transition 例項)。

       TransitionManager.go()啟動動畫的時候,場景一般通過佈局檔案給出。場景例項的獲取則通過Scene.getSceneForLayout()來獲取,需要三個引數:第一個引數代表場景所在的ViewGroup、第二個引數代表場景佈局檔案、第三個引數佈局檔案轉換View所需要的Content。

TransitionManager.go()的場景是通過佈局檔案指定。

1.2、beginDelayedTransition()開啟場景動畫

用beginDelayedTransition()實現場景動畫的效果圖


beginDelayedTransition實現場景動畫

      通過TransitionManager.beginDelayedTransition()也可以開啟場景動畫。在執行TransitionManager.beginDelayedTransition()之後,系統會儲存一個當前檢視樹狀態的場景,之後當我們改變了View的屬性之後(比如重新設定了View位置、縮放、clipe等等)。在下一次繪製時,系統會自動對比之前儲存的檢視樹,然後執行相應動畫。

二、Activity過渡動畫

2.1、API 21之前Activity過渡動畫使用

      API21之前Activity過渡動畫通過兩種方式來實現:style主題裡面統一設定、或者使用程式碼overridePendingTransition函式單獨設定。(當然了程式碼設定的優先順序要高於style主題裡面統一設定)

  • style檔案主題裡面統一定義,全域性為所有Activity設定過渡動畫效果。
<item name="android:windowAnimationStyle">@style/Animation.Activity.Customer</item>
    <style name="Animation.Activity.Customer" parent="@android:style/Animation.Activity">
        <!-- 進入一個新的Activity的時候,A->B B進入動畫 -->
        <item name="android:activityOpenEnterAnimation">@anim/right_in</item>
        <!-- 進入一個新的Activity的時候,A->B A退出動畫 -->
        <item name="android:activityOpenExitAnimation">@anim/left_out</item>
        <!-- 退出一個Activity的時候,B返回到A A進入動畫 -->
        <item name="android:activityCloseEnterAnimation">@anim/left_in</item>
        <!-- 退出一個Activity的時候,B返回到A B退出動畫 -->
        <item name="android:activityCloseExitAnimation">@anim/right_out</item>
    </style>
  • 程式碼overridePendingTransition(enterAnim, exitAnim)函式單獨設定

     
 overridePendingTransition()函式的使用,關鍵是理解兩個引數所代表的是誰的動畫。假設有這麼一個例項A啟動B,那麼第一個引數就是B進入時候的動畫、第二個引數就是A退出時候的動畫。

關於overridePendingTransition函式,有一個需要注意的地方就是:它必需緊挨著startActivity()或者finish()或者onBackPressed()函式呼叫,否則不一定有效果。

2.2、API 21 之後Activity過渡動畫使用

Activity過渡動畫效果圖


這裡寫圖片描述

     
 But 現在你又可以獲取一個新的技能了。在API 21之後google又推出了一種比之前效果更加讚的過渡動畫。 通過ActivityOptions + Transition來實現Activity過渡動畫。

     
 在講使用ActivityOptions + Transition來實現Activity過渡動畫之前先來了看下ActivityOptions裡面幾個函式代表啥意思。

/**
 * 和overridePendingTransition類似,設定跳轉時候的進入動畫和退出動畫
 */
public static ActivityOptions makeCustomAnimation(Context context, int enterResId, int exitResId);

/**
 * 通過把要進入的Activity通過放大的效果過渡進去
 * 舉一個簡單的例子來理解source=view,startX=view.getWidth(),startY=view.getHeight(),startWidth=0,startHeight=0
 * 表明新的Activity從view的中心從無到有慢慢放大的過程
 */
public static ActivityOptions makeScaleUpAnimation(View source, int startX, int startY, int width, int height);

/**
 * 通過放大一個圖片過渡到新的Activity
 */
public static ActivityOptions makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY);

/**
 * 場景動畫,體現在兩個Activity中的某些view協同去完成過渡動畫效果,等下在例子中能更好的看到效果
 */
public static ActivityOptions makeSceneTransitionAnimation(Activity activity, View sharedElement, String sharedElementName);

/**
 * 場景動畫,同上是對多個View同時起作用
 */
public static ActivityOptions makeSceneTransitionAnimation(Activity activity, android.util.Pair<View, String>... sharedElements);

       上面給出了四類方式的使用,個人覺得makeCustomAnimation、makeScaleUpAnimation、makeThumbnailScaleUpAnimation這三種產生的效果還是走的API 21之前的效果,而且這三種效果好像和Transition動畫沒啥太多的聯絡。我們用的最多的還是makeSceneTransitionAnimation()函式,makeSceneTransitionAnimation效果才是和Transition動畫效果密切相關的。所以我們重點來看makeSceneTransitionAnimation的使用。

       Transitionz過渡動畫的使用也是有前提的:

  • API 21以上。當然你也可以不使用ActivityOptions,而是使用相容類ActivityOptionsCompat來替換ActivityOptions。(相容類給到我們的作用是保證程式在低版本執行不會掛掉,但是不能保證低版本也能起到響應的效果的)
  • 必須允許Activity可以使用Transition,要麼在style裡面設定(true),要麼直接通過程式碼設定(getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);)。

       對於Transition Activity過渡動畫的使用,我們簡單的分為三個步驟:告訴系統以Transition的方式啟動Activity、定義過渡動畫、設定過渡動畫。

2.2.1 告訴系統以Transition的方式啟動Activity

       啟動Activity的方式和之前不一樣了,要告訴Activity以Transition的方式啟動。先要得到ActivityOptions,而且呼叫的startActivity()還和之前不一樣了,是呼叫兩個引數的startActivity(),第二個引數是ActivityOptions.toBundler。(當然為了相容之前的版本你也可以使用相容類ActivityOptionsCompat, ActivityCompat)。如下述程式碼所示。

API 21 之後的程式碼

ActivityOptions compat = ActivityOptions.makeSceneTransitionAnimation(mActivity);
startActivity(new Intent(mContext, MakeSceneTransitionctivity.class), compat.toBundle());

使用ActivityOptionsCompat相容API 21之前的程式碼

ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(mActivity);
ActivityCompat.startActivity(mContext, new Intent(mContext, MakeSceneTransitionActivity.class), compat.toBundle());
2.2.2、定義過渡動畫

系統給我們提供了三種Transition過渡動畫,可以拿來直接使用。

系統預設動畫 解釋
分解(explode) 從場景中心移入或移出檢視
滑動(slide) 從場景邊緣移入或移出檢視
淡入淡出(fade) 通過調整透明度在場景中增添或移除檢視

       我們有兩種方式來定義過渡動畫:資原始檔的方式、程式碼的方式。

  • 資原始檔的方式定義過渡動畫

       在 res/ 目錄下建立transition 資原始檔夾後,就可以在該資料夾下對每一種動畫進行定義。

       一般我們可以寫成如下的形式:

<?xml version="1.0" encoding="utf-8"?>
<slide xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000">
    <targets>
        <target android:excludeId="@android:id/statusBarBackground"/>
        <target android:excludeId="@android:id/navigationBarBackground"/>
    </targets>
</slide>

該xml定義了一個slide過渡動畫,除了狀態列、導航欄以外其他所有的View進行滑入滑出動畫效果。

       其中 是動畫效果的名稱,當然你也可以換系統其他兩種過渡動畫(explode、fade),或者高檔一點直接自己自定義一個過渡動畫。每一種動畫效果,都有額外的屬性。比如滑動 slide動畫,可以使用 android:slideEdge=”top” 設定滑動的方向;淡入淡出fade動畫,可以使用 android:fadingMode=”fade_in” 設定具體是淡入(fade_in)還是淡出(fade_out)等等。

        標籤讓我們可以更加靈活的控制動畫。targets標籤裡面可以定義需要轉場(或者不需要轉場)的目標 id ,這個 id 可以是系統自帶的,也可以是我們自己檢視中的 view 的 id,每一個 id 需要單獨在 標籤中定義,android:targetId 表示目標 id 需要進行過渡轉換的 view,而 android:excludeId 表示我們不需要該 id 的 view 進行過渡轉場。

       上面只是定義了一種過渡動畫,如果我們想要在同一個過渡狀態中實現兩種或多種動畫效果怎麼辦?也簡單,將根標籤替換為 ,然後定義每一種動畫效果,最後記得在根標籤中使用 android:transitionOrdering 註明這幾種動畫的演示順序:sequential 表示順序執行、 together 表示同時執行。比如像下面的程式碼:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:transitionOrdering="sequential">
    <slide android:slideEdge="bottom">
        <targets>
            <target android:targetId="@id/image_shared" />
        </targets>
    </slide>

    <fade>
        <targets>
            <target android:excludeId="@android:id/statusBarBackground" />
            <target android:excludeId="@android:id/navigationBarBackground" />
            <target android:excludeId="@id/image_shared" />
        </targets>
    </fade>
</transitionSet>

該xml定義了兩個過渡動畫slide、fade。順序執行。slide 動畫針對 id 為 image_shared 的 view 進行下面滑入,fade 動畫將除了狀態列、導航欄和 id 為image_shared 以外的 view,進行淡入淡出。

  • 程式碼的方式定義過渡動畫
           可以用資原始檔來定義的物件,那我們們就一定可以用程式碼的方式來實現,我們用程式碼來實現上述多種動畫效果對應的資原始檔:
//      //資原始檔指定過渡動畫
//      getWindow().setEnterTransition(TransitionInflater.from(this).inflateTransition(R.transition.transition_target));
//程式碼制定過渡動畫
TransitionSet transitionSet = new TransitionSet();
//slide動畫
Slide slide = new Slide(Gravity.BOTTOM);
slide.addTarget(R.id.image_shared);
transitionSet.addTransition(slide);
//fade動畫
Fade fade = new Fade();
fade.excludeTarget(android.R.id.statusBarBackground, true);
fade.excludeTarget(android.R.id.navigationBarBackground, true);
fade.excludeTarget(R.id.image_shared, true);
transitionSet.addTransition(fade);
transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
getWindow().setEnterTransition(transitionSet);

程式碼和資原始檔幾個對應函式

程式碼對應 xml對應 解釋
addTarget() android:targetId 指定目標View,讓目標View參與動畫
excludeTarget() android:excludeId 是否過濾指定View(是否參與動畫)
excludeChildren() 是否過濾指定ViewGroup的子View(是否參與動畫)

       Transition動畫已經定義出來了,還不夠,有的時候還得監聽動畫的啟動過程。這個時候就是TransitionListener登場的時候了。TransitionListener的使用就太容易了,聰明的你一看TransitionListener裡面各個函式的名字就能知道怎麼使用了。

2.2.3、設定過渡動畫

       告訴Activity以Transition的方式啟動了,也定義好了過渡動畫了。接下來就是去設定過渡動畫了。Transition過渡動畫的設定可以在style檔案中統一設定也可以在程式碼中設定(程式碼中設定的優先順序比style主題檔案優先順序高)。

程式碼指定 style主題指定 解釋
getWindow().setEnterTransition() android:windowEnterTransition A啟動B,B中的View進入場景的transition(程式碼所在位置B)
getWindow().setExitTransition() android:windowExitTransition A啟動B,A中的View退出場景的transition(程式碼所在位置A)
getWindow().setReturnTransition() android:windowReturnTransition B返回A,B中的View退出場景的transition(程式碼所在位置B)
getWindow().setReenterTransition() android:windowReenterTransition B返回A,A中的View重新進入場景的transition(程式碼所在位置A)

       Activity過渡動畫使用的時候有一個設定可以提高展示效果,可以通過在主題中設定windowAllowEnterTransitionOverlap、windowAllowReturnTransitionOverlap讓動畫過渡的更加自然。其中windowAllowEnterTransitionOverlap表示進入動畫是否可以覆蓋別的動畫、windowAllowReturnTransitionOverlap表示返回動畫是否可以覆蓋別的動畫。

三、Shared Element Transition(共享元素過渡動畫)

簡單共享元素動畫效果圖


共享元素簡單例項

更新共享元素動畫效果圖


共享元素-圖片瀏覽例項

3.1、共享元素基本概念

       你可能有發現前面講的Activity過渡動畫的例項中,ActivityOptions類裡面makeSceneTransitionAnimation()函式後面的引數我們都沒有傳遞進去,其實後面的引數是在使用共享元素的時候才會使用到的,接下來的例項這些個引數就會排上用場了。

       當你想要從一個Activity A轉換到Activity B,而且他們共享一個元素(比如是一個view),在這種場景下,最好的使用者體驗可能就是將共享的元素直接變換到最終的地方和大小,這會使使用者專注於應用而且有一種連貫性的表達。

       共享元素的連線點是所有共享元素View的transition name。它可以在layout檔案裡面設定(android:transitionName)、也可以程式碼設定(View.setTransitionName(ImageConstants.IMAGE_SOURCE[mCurrentPosition]);)。通過transtion name來判斷哪兩個元素是共享關係。

       有了前面Activity過渡動的理解,共享元素動畫在理解上就簡單的多了。同Activity過渡動畫一樣,共享元素的動畫也可以通過程式碼或者主題檔案來設定(Fragment裡面共享元素動畫的設定可以類比Activity裡面共享元素動畫的設定),如下所示。

程式碼指定 style主題指定 解釋
getWindow().setSharedElementEnterTransition() android:windowSharedElementEnterTransition A啟動B,B中的View共享元素的transition(程式碼所在位置B)
getWindow().setSharedElementExitTransition() android:windowSharedElementExitTransition A啟動B,A中的View共享元素transition(程式碼所在位置A)
getWindow().setSharedElementReturnTransition() android:windowSharedElementReturnTransition B返回A,B中的View共享元素的transition(程式碼所在位置B)
getWindow().setSharedElementReenterTransition() android:windowSharedElementReenterTransition B返回A,A中的View重新進入共享元素的transition(程式碼所在位置A)

       同樣和Activity過渡動畫一樣的也可以給Transiton設定回撥監聽,比如監聽Transition開始和結束等等。

3.1、更新共享元素對應關係

       有這種情況,比如我們第一個介面是一個列表(RecyclerView)每個item都是一個圖片,點選進入另一個頁面詳情頁面,詳情頁面呢有是ViewPager的形式。可以左右滑動。我們們有的時候就想,就算詳情介面滑動到了其他照片,在返回到第一個頁面的時候也想要有共享元素動畫的效果。這個時候就得更新下共享元素的對應關係了。

       怎麼更新呢,關鍵是看SharedElementCallback類的onMapSharedElements()函式,這個函式是用來裝載共享元素的。比如有這麼個情況,還是上面的例子A介面跳轉到B介面。那麼A介面在B返回的時候要更新下、B介面在返回之前要更新下。所以給A介面設定setExitSharedElementCallback(SharedElementCallback);、給B介面設定setEnterSharedElementCallback(SharedElementCallback)。其他更多的細節可以參考下例項程式碼中的實現。

setExitSharedElementCallback(SharedElementCallback)的SharedElementCallback裡面的onMapSharedElements()函式在Activity exit和reenter時都會觸發
setEnterSharedElementCallback(SharedElementCallback)的SharedElementCallback裡面的onMapSharedElements()函式在Activity enter和return時都會觸發。

3.1、延時共享元素動畫

       有的時候又有這種情況,比如要展示一個網路圖片,在網路圖片獲取到之前,這個共享元素的動畫效果沒啥作用。怎麼的也得等我圖片獲取完成之後在開始共享元素的動畫效果吧,這個時候延時元素動畫就派上大用場了。

       postponeEnterTransition()函式用於延時動畫,startPostponedEnterTransition()函式用於開始延時的共享動畫。那我們們就可以這麼幹了,在Activity進入的時候先呼叫postponeEnterTransition()延時動畫,在網路圖片獲取完成之後在呼叫startPostponedEnterTransition()開始動畫。具體可以參考下例項程式碼中的實現。

       由於自己的能力有限上面也只是簡單的介紹了下Transition基本的使用,好讓自己入個門,Transition裡面的東西遠不止這些,等待大家去發掘。最後給出本文中所有涉及到的例項下載地址:Android Transiton 例項程式碼下載地址

相關文章