Android 過渡動畫

 

標註

過渡動畫最好都在Activity之間實現

 

Android 的過渡動畫可以分爲四個部分:

  1. Activity/Fragment 切換時的內容過渡動畫(content transition)

  2. Activity/Fragment 切換時的共享元素過渡動畫

  3. 同一個頁面中的場景過渡動畫

  4. 共享元素過渡動畫 + 揭露效果(circular reveal)

 

首先先講解一下Transition 

Lollipop(5.0) 中 Activity 和 Fragment 的過渡動畫是基於 Android 一個叫作 Transition 的新特性實現的。 初次引入這個特性是在 KitKat(4.4) 中,Transition 框架提供了一個方便的 API 來構建應用中不同 UI 狀態切換時的動畫。 這個框架始終圍繞兩個關鍵概念:場景(scenes)和轉換(transitions)。場景定義了一個確定的 UI 狀態,而轉換定義了兩個場景切換時的動畫。

當兩個場景切換時,Transition 主要有下面兩個行爲:

(1)確定開始場景和結束場景中每個 view 的狀態。

(2)根據狀態差異創建 Animator,用以場景切換時 view 的動畫。

 

Activity/Fragment 切換時的內容過渡動畫

當從Activity A跳轉到Activity B,內容佈局將根據定義好的過渡做動畫變換,在android過渡動畫預先定義了三種過渡動畫,分別是Explode,SlideFade,這些過渡動畫會跟蹤當前設置的View,並執行相應的動畫。

  • 分解(Explode) - 從場景中心移入或移出視圖

  • 滑動(Slide)- 從場景邊緣移入或移出視圖

  • 淡入淡出(Fade)- 通過調整透明度在場景中增添或移除視圖

根據上述情景,可以爲activity劃分出4種場景的動畫:

  • Activity A 的 退出動畫(ExitTransition ),即 A 啓動 B 時 A 中 View 的動畫
  • Activity B 的 進入動畫(EnterTransition ),即 A 啓動 B 時 B 中 View 的動畫
  • Activity B 的 返回動畫(ReturnTransition),即 B 返回 A 時 B 中 View 的動畫
  • Activity A 的 重入動畫(ReenterTransition), 即 B 返回 A 時 A 中 View 的動畫

實現步驟

1.打開窗口內容轉換開關    

  • 在風格定義中設置:
<style name="BaseAppTheme" parent="Theme.AppCompat.Light">
  <!-- enable window content transitions -->
  <item name="android:windowContentTransitions">true</item>
  //還要加一條,不然會出現白屏哦
  <item name="android:windowIsTranslucent">true</item>
</style>
  • 在代碼中設置:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState)
    getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
}

2.指定進入、退出轉換

  • 在風格定義中設置:(標註:如果沒有指定returnTransitionreenterTransition,返回 MainActivity 時會分別執行反轉的進入和退出轉換)
<style name="BaseAppTheme" parent="Theme.AppCompat.Light">
  <!-- specify enter and exit transitions -->
  <item name="android:windowEnterTransition">@transition/activity_enter</item>
  <item name="android:windowExitTransition">@transition/activity_exit</item>
</style>

      轉換文件存放在res/transition目錄下

      activity_enter.xml

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@integer/transition_time" android:interpolator="@android:interpolator/decelerate_quad">
    <slide android:slideEdge="right" >
    </slide>
</transitionSet>

      activity_exit.xml

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@integer/transition_time" android:interpolator="@android:interpolator/decelerate_quad">
    <slide android:slideEdge="right" >
    </slide>
</transitionSet>

 ​​​​​

  • 在代碼中設置:

      Activity A

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_transition);
    setupWindowAnimations();
}

private void setupWindowAnimations() {
    // inflate from xml
    Slide slide = TransitionInflater.from(this).inflateTransition(R.transition.activity_slide);
    // or create directly
    Slide slide = new Slide();
    slide.setDuration(1000);

    getWindow().setExitTransition(slide);
}

      Activity B

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_transition);
    setupWindowAnimations();
}

private void setupWindowAnimations() {
    // inflate from xml
    Fade fade = TransitionInflater.from(this).inflateTransition(R.transition.activity_fade);
    // or create directly
    Fade fade = new Fade();
    fade.setDuration(1000);

    getWindow().setEnterTransition(fade);
}

 

3.調用跳轉命令

ActivityCompat.startActivity(TransitionActivity.this, intent, ActivityOptionsCompat.makeSceneTransitionAnimation(TransitionActivity.this).toBundle());

 

內容過渡動畫的原理

內容過渡動畫的創建之前,必須要確定視圖的狀態信息,而這需要修改所有過渡視圖(transitioning view)的可見性。當 Activity A 啓動 Activity B 時,下面的事件將會發生:

1)  Activity A 調用 startActivity()

  1. 系統遍歷 A 的視圖節點,找到將要運行退退出轉換的所有過渡視圖

  2. A 的退出轉換記錄所有過渡視圖的開始狀態

  3. 系統將所有過渡視圖的可見性設置爲INVISIBLE

  4. 在下一幀,A 的退出轉換記錄所有過渡視圖的結束狀態

  5. A 的退出轉換比較每個過渡視圖的開始狀態和結束狀態,然後創建 Animator 作爲退出動畫,運行該動畫。

2)Activity B 啓動了

  1. 系統遍歷 B 的視圖節點,找到將要運行進入轉換的所有過渡視圖,設置這些過渡視圖的可見性爲INVISIBLE

  2. B 的進入轉換記錄所有過渡視圖的開始狀態

  3. 系統將所有過渡視圖的可見性設置爲VISIBLE

  4. 在下一幀,B 的進入轉換記錄所有過渡視圖的結束狀態

  5. B 的進入轉換比較每個過渡視圖的開始狀態和結束狀態,然後創建 Animator 作爲進入動畫,運行該動畫。

通過上面的分析可以知道,所有的內容轉換都需要記錄每個過渡視圖的開始狀態和結束狀態。而抽象類Visibility已經做了這部分內容了,Visibility的子類只需要實現 onAppear() 和 onDisappear() 方法,創建過渡視圖進入或退出場景的 Animator。Android 5.0 中Visibility有三個子類 -- Fade、Slide、Explode,如果有需要的話也可以自定義Visibility子類。

Transitioning View & Transition Groups

這個小節將討論系統如何找出過渡視圖,以及通過 transition group 自定義過渡視圖。

在轉換開始前,系統通過遍歷視圖節點遞歸尋找過渡視圖,這個遞歸查找是從對根視圖調用ViewGroup.captureTransitioningViews方法開始的:

/** @hide */
@Override
public void captureTransitioningViews(List<View> transitioningViews) {
    if (getVisibility() != View.VISIBLE) {
        return;
    }
    if (isTransitionGroup()) {
        transitioningViews.add(this);
    } else {
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            child.captureTransitioningViews(transitioningViews);
        }
    }
}

上面的遞歸查找會遍歷整個視圖樹節點,直到找到一個 VISIBLE 的 View 或者一個 transition group。Transition group 允許我們把整個 ViewGroup 當作一個整體在運行動畫時,所有的子視圖都會一起作爲一個元素進行動畫。否則的話,遞歸繼續進行,所有子視圖會單獨對待。

public boolean isTransitionGroup() {
    if ((mGroupFlags & FLAG_IS_TRANSITION_GROUP_SET) != 0) {
        return ((mGroupFlags & FLAG_IS_TRANSITION_GROUP) != 0);
    } else {
        final ViewOutlineProvider outlineProvider = getOutlineProvider();
        return getBackground() != null || getTransitionName() != null ||
                (outlineProvider != null && outlineProvider != ViewOutlineProvider.BACKGROUND);
    }
}

默認情況下,有非空背景、transitionName 不爲空、outlineProvider 不爲空且不等於 ViewOutlineProvider.BACKGROUND 的 ViewGroup 會當作 Transition Group。不過我們也可以通過setTransitionGroup(boolean)來設置是否爲 Transition Group。特別在運行 WebView 相關的過渡動畫時,需要調用webview.setTransitionGroup(true)來將 WebView 當作一個整體。

Transition Overlap

默認情況下,內容過渡動畫的 進入/返回 轉換會在 退出/重新進入 轉換結束前一點點開始,產生一個小的重疊來讓整體的效果更自然、更協調。這個行爲其實可以通過 Window/Fragment 的setAllowEnterTransitionOverlap(boolean)setAllowReturnTransitionOverlap(boolean)方法來設置,默認 overlap 是 true,進入轉換會退出轉換開始後儘可能快地開始,如果設置爲 false,進入轉換只能在退出轉換結束後開始。這個對 Activity 和 Fragment 的共享元素過渡動畫也是生效的。

<style name="BaseAppTheme" parent="Theme.AppCompat.Light">
  <!-- specify transition overlap -->
  <item name="android:windowAllowEnterTransitionOverlap">true</item>
  <item name="android:windowAllowReturnTransitionOverlap">true</item>
</style>

Activity/Fragment 切換時的共享元素過渡動畫

Activity/Fragment 切換時的內容過渡動畫分爲 calling Activity 的退出動畫和 called Activity 的進入動畫,兩個動畫實際上是沒有什麼關係的。而共享元素過渡動畫,在確定開始狀態和結束狀態是分別在兩個頁面上的,可以實現共享元素從一個頁面到另一個頁面的動畫。

Android 5.0(API 21)默認提供了下面四種恭喜元素轉換:

  • changeBounds - 爲目標視圖的佈局佈局邊界的變化添加動畫

  • changeClipBounds - 爲目標視圖的裁剪邊界的變化添加動畫

  • changeTransform - 爲目標視圖的縮放與旋轉變化添加動畫

  • changeImageTransform - 爲目標圖像的大小與縮放變化添加動畫

與內容過渡動畫類似,共享元素過渡動畫也分爲 sharedElementExitTransition、sharedElementEnterTransition、sharedElementReturnTransition、sharedElementReenterTransition。

實現步驟

1.打開窗口內容轉換開關

  • 在風格定義中設置:
<style name="BaseAppTheme" parent="Theme.AppCompat.Light">
  <!-- enable window content transitions -->
  <item name="android:windowContentTransitions">true</item>
</style>
  • 在代碼中設置:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState)
    getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
}

2.指定共享元素轉換

  • 在風格定義中設置:
<style name="BaseAppTheme" parent="Theme.AppCompat.Light">
  <!-- specify shared element transitions -->
  <item name="android:windowSharedElementEnterTransition">@transition/changebounds</item>
  <item name="android:windowSharedElementExitTransition">@transition/changebounds</item>
</style>
  • 在代碼中設置:

下面代碼中是指定 Activity 共享元素轉換,Fragment 的話只需要改爲 Fragment 的同名方法即可。

Activity A

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_transition);
    setupWindowAnimations();
}

private void setupWindowAnimations() {
    // inflate from xml
    ChangeImageTransform changeImageTransform = TransitionInflater.from(this).inflateTransition(R.transition.changebounds);
    // or create directly
    ChangeImageTransform changeImageTransform = new ChangeImageTransform();

    getWindow().setSharedElementExitTransition(changeImageTransform);
}

Activity B

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_shared_element);
    setupWindowAnimations();
}

private void setupWindowAnimations() {
    // inflate from xml
    ChangeImageTransform changeImageTransform = TransitionInflater.from(this).inflateTransition(R.transition.changebounds);
    // or create directly
    ChangeImageTransform changeImageTransform = new ChangeImageTransform();

    getWindow().setSharedElementEnterTransition(changeImageTransform);
}

 

3.爲共享元素指定統一的 transition name

在佈局文件中使用android:transitionName屬性或者在代碼中使用View.setTransitionName(String)方法來爲兩個頁面的共享元素指定同樣 的transitionName。

Activity A.xml(實際上在跳轉時也要再上view和transition name,所以在ActivityA中不設置也沒問題)

<ImageView
        android:id="@+id/small_blue_icon"
        style="@style/MaterialAnimations.Icon.Small"
        android:src="@drawable/circle"
        android:transitionName="@string/blue_name" />

Activity B.xml

<ImageView
        android:id="@+id/big_blue_icon"
        style="@style/MaterialAnimations.Icon.Big"
        android:src="@drawable/circle"
        android:transitionName="@string/blue_name" />

 

4a.啓動 Activity 時帶上共享元素參數

使用ActivityOpstions.makeSceneTransitionAnimation()方法來指定共享元素的 origin view 和 transition name。如果要結束第二個 Activity 時也顯示共享元素過渡動畫,請調用Activity.finishAfterTransitino()而非Activity.finish()

blueIconImageView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent i = new Intent(MainActivity.this, SharedElementActivity.class);

        View sharedView = blueIconImageView;
        String transitionName = getString(R.string.blue_name);

        ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, sharedView, transitionName);

        // version may be lower than Android 5.0
        ActivityOptionsCompat transitionActivityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, sharedView, transitionName);

        startActivity(i, transitionActivityOptions.toBundle());

        
    }
});

效果演示:(只是借鑑,並不要求一樣)

 

4b.切換 Fragment 時帶上共享元素參數

在 FragmentTransaction 提交前使用FragmentTransaction.addSharedElement()方法加上共享元素信息。

getFragmentManager().beginTransaction() .replace(R.id.content, fragmentB) .addSharedElement(blueView, getString(R.string.blue_name)) .commit();

補充:

更新共享元素對應關係

很多情況下,我們需要共享元素的頁面滿足下面的條件:calling是一個列表頁面,called是一個詳情頁,我們在列表中選擇一項打開,然後會有一個元素共享過渡到詳情頁,而我們在詳情頁是可以左右滑動切換元素的,如下圖所示:


這個系統自帶的相冊APP,可以看到我們進入大圖預覽時,點擊的是圖片4,然後我們左右滑動,切換圖片之後返回列表頁,這個時候過渡動畫直接返回到了圖片1,那這是怎麼做到的呢?

其實這裏無非在過渡動畫返回時,需要告訴系統,我的綁定關係改變了,我們可以在calling設置一個SharedElementCallback,然後在回調的onMapSharedElements更新一下對應關係,大概的邏輯代碼如下:

setExitSharedElementCallback(new SharedElementCallback() {
        @Override
        public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
            super.onMapSharedElements(names, sharedElements);      
            sharedElements.put("avator",getItemByPosition(curentPos));
        }
    });

我們在celled中左右滑動修改了列表Index之後,需要通過某種方式告知calling,你可以通過startActivityForResult()也可以通過EventBus之類的第三方庫,但是需要注意的是,onMapSharedElementsexitreenter時都會觸發!

延遲共享元素

想想這種場景吧,called中的共享元素在進入時,並沒有到達最終的位置(比如這個數據需要從網絡獲取數據之後,才展示它真正的大小位置),此時如果過渡動畫如果過早的執行,那麼過渡動畫獲取的called中共享view的狀態並不正確,所以我們有時候希望called中的共享元素完全layout完畢之後再執行,幸好API提供了支持。

calledonCreate中我們可以這樣處理:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 延遲共享動畫的執行
postponeEnterTransition();
}

然後在共享元素的最終佈局確定後,你可以執行startPostponedEnterTransition來啓動共享元素動畫,我們一般可以通過下面的工具方法來啓動延遲動畫:

private void scheduleStartPostponedTransition(final View sharedElement) {
sharedElement.getViewTreeObserver().addOnPreDrawListener(
    new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            //啓動動畫       
           sharedElement.getViewTreeObserver().removeOnPreDrawListener(this);
            startPostponedEnterTransition();
            return true;
        }
    });
}

但是使用延遲共享動畫有兩點需要注意:
      1. 調用postponeEnterTransition必須要在適當的時候調用startPostponedEnterTransition,否則Activity的過渡就會卡到這裏,不能執行下去

      2.基於1,我們在這兩個方法間實際上也不能間隔太久(前面說的等待網絡返回只是舉個例子,不要在實際上使用等待網絡返回的共享元素),如果間隔太大,那頁面的過渡會直到調用startPostponedEnterTransition才能進行頁面切換!

 

共享元素過渡動畫的原理

和內容過渡動畫類似的是,系統也會在創建共享元素動畫前直接修改共享元素視圖的屬性。當 Activity A 啓動 Activity B 時,下面的事件將會發生:

  1. A 調用startActivity(intent, bundle)後,B 啓動時,窗口的背景是透明的。

  2. 系統以 A 爲標準重新設置 B 的每個共享元素視圖的大小和位置,過一會 B 的進入轉換會記錄 B 中所有共享元素的開始狀態,而對於內容過渡來說,其他的 transitioning view 的可見性都是 INVISIBLE。

  3. 系統再重新將 B 的每個共享元素視圖的大小和位置設置爲原來的樣子,過一會 B 的進入轉換會記錄 B 中所有共享元素的結束狀態。

  4. B 的進入轉換比較每個共享元素的開始狀態和結束狀態,創建 Animator 作爲共享元素動畫。

  5. 系統將隱藏 A 的所有共享元素視圖,然後開始運行 B 的共享元素動畫。在 B 的共享元素動畫過程中,B 的窗口背景會逐漸變爲不透明的。

對比內容過渡動畫,內容過渡動畫中系統會修改 transition views 的可見性,而共享元素過渡動畫中系統會修改 shared element views 的位置、大小和顯示。而且我們也可以看出實際上共享元素的 view 其實並沒有在 Activity/ Fragment 之間共享,事實上,我們看到的進入或者返回的共享元素過渡動畫都是直接在 B 的視圖中運行的。

 

Shared Element Overlay

默認情況下,共享元素視圖是繪製在整個視圖結構之上的 -- 窗口的 ViewOverlay 層。ViewOverlay 是在 Android 4.3(API 18)引入的,方便在視圖上面繪製內容。添加到視圖的 ViewOverlay 層的 Drawables 或者 Views 都會在所有其他視圖上面繪製,不會被遮蓋。而共享元素默認會在 ViewOverlay 層繪製的原因是:共享元素是整個進入轉換中的焦點,如果 transitioning view 忽然遮蓋了共享元素的話,整體的效果會大打折扣。

雖然共享元素視圖默認是繪製在 ViewOverlay 層的,但是也可以在必要情況下使用 Window.setSharedElementsUseOverlay(false) 來禁用。除非真的有需要,最好修改這個,否則共享元素過渡動畫可以會出現預料之外的效果。

 

更新共享元素對應關係

想象這樣這個場景:GalleryActivity 裏面有一個圖片列表 RecyclerView,點擊進入 PreviewActivity,PreviewActivity 裏面是一個 ViewPager,每項都顯示圖片,可以左右滑動。那麼如何實現在左右滑動後返回 GalleryActivity 也能有相應的共享元素動畫呢?效果如下圖:

shared_element_anim2.gif

下面一步一步來實現:

1. PreviewActivity 的 ViewPager 滑動時更新共享元素

通過Activity.setEnterSharedElementCallback給 PreviewActivity 設置一個自定義的 SharedElementCallback,實現onMapSharedElements方法,當 ViewPager 滑動項時更新共享元素。

2. 返回 GalleryActivity 更新共享元素

通過Activity.setExitSharedElementCallback給 GalleryActivity 設置一個自定義的 SharedElementCallback,再實現onActivityReenter回調,在回調中更新共享元素(需要 PreviewActivity 返回時 setResult())。

3. 返回 GalleryActivity 時 RecyclerView 滑動到相應圖片位置

onActivityReenter回調讓 RecyclerView 滑動到相應圖片位置。

這個小節的更多實現細節,請看 “Dynamic” Shared Element Transition: How to hold a common gallery flow

 

同一個頁面中的場景過渡動畫

前面介紹的內容過渡動畫和共享元素過渡動畫都是在不同頁面間的過渡動畫,其實過渡動畫也可以在同一個頁面中,只要開始場景和結束場景在同一個頁面,那麼創建的過渡動畫就會在一個頁面中運行。

在一個頁面中確定開始場景和結束場景有兩種方式:手動創建 Scene 和系統自動監聽佈局變更。

 

1.手動創建 Scene

我們可以通過 Scene 類來手動創建場景,在創建 Scene 實例時需要指定一個sceneRoot,在調用TransitionManager.go()變換場景時,轉換先記錄sceneRoot子節點下所有 transitioning view 的狀態作爲開始場景,然後移除sceneRoot下的所有視圖,將新場景的佈局添加到sceneRoot,再記錄sceneRoot子節點下的所有 transitioning view 的狀態作爲結束場景,最後根據兩個場景創建並運行過渡動畫。

scene1 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene1, this);
scene2 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene2, this);
scene3 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene3, this);
scene4 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene4, this);

(...)

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.button1:
            TransitionManager.go(scene1, new ChangeBounds());
            break;
        case R.id.button2:
            TransitionManager.go(scene2, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds));
            break;
        case R.id.button3:
            TransitionManager.go(scene3, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential));
            break;
        case R.id.button4:
            TransitionManager.go(scene4, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential_with_interpolators));
            break;  
    }
}

演示效果

 

2.系統自動監聽佈局變更

原理和上面一種其實是一樣的。

 

2.1、開始延遲轉換

TransitionManager.beginDelayedTransition(sceneRoot);

TransitionManager.beginDelayedTransition(sceneRoot) 時會馬上記錄sceneRoot子節點下所有 transitioning view 的狀態作爲開始場景,然後在下一幀中再次記錄sceneRoot子節點下所有 transitioning view 的狀態作爲結束場景,所以在下一幀之前我們修改佈局屬性可以被感知到,因此根據這些差異轉換可以創建並運行過渡動畫。

 

2.2、修改視圖的佈局屬性

ViewGroup.LayoutParams params = greenIconView.getLayoutParams();
params.width = 200;
greenIconView.setLayoutParams(params);

 

共享元素過渡動畫 + 揭露效果(circular reveal)

在 Android 5.0(API 21)中也引入揭露效果(circular reveal),當你顯示或隱藏一組 UI 元素時,揭露動畫可爲用戶提供視覺連續性。使用ViewAnimationUtils.createCircularReveal()方法可以爲裁剪區域添加動畫以揭露或隱藏視圖。更詳細的介,紹請看官網文檔 -- 使用揭露效果

揭露動畫可以和共享元素過渡動畫結合起來創造更有意義的動畫,來引導用戶發生了什麼。看下面這個效果:

實現步驟:

  • 橙色的圓圈是 MainActivity 跳轉到 RevealActivity 的共享元素。

  • RevealActivity 的共享元素轉換有一個 listener 監聽共享元素轉換的結束。監聽到轉換結束後,做了兩件事情:(1)在 Toolbar 上運行了揭露動畫;(2)對 RevealActivity 底部的四個按鈕運行了放大動畫。

1.監聽共享元素轉換結束

Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion);
getWindow().setSharedElementEnterTransition(transition);
transition.addListener(new Transition.TransitionListener() {
    @Override
    public void onTransitionEnd(Transition transition) {
        animateRevealShow(toolbar);
        animateButtonsIn();
    }
    
    (...)

});

2.Toolbar 的揭露效果

private void animateRevealShow(View viewRoot) {
    int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
    int cy = (viewRoot.getTop() + viewRoot.getBottom()) / 2;
    int finalRadius = Math.max(viewRoot.getWidth(), viewRoot.getHeight());

    Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, cx, cy, 0, finalRadius);
    viewRoot.setVisibility(View.VISIBLE);
    anim.setDuration(1000);
    anim.setInterpolator(new AccelerateInterpolator());
    anim.start();
}

3.底部四個按鈕的放大動畫

private void animateButtonsIn() {
    for (int i = 0; i < bgViewGroup.getChildCount(); i++) {
        View child = bgViewGroup.getChildAt(i);
        child.animate()
                .setStartDelay(100 + i * DELAY)
                .setInterpolator(interpolator)
                .alpha(1)
                .scaleX(1)
                .scaleY(1);
    }
}

 


參考鏈接:

https://www.jianshu.com/p/69d48f313dc4

https://www.jianshu.com/p/50f62d9e60e1

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章