★20.轉場動畫

普通轉場動畫

1. 準備工作

方式一

Activity.onCreate()setContentView()前調用以下代碼。

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

方式二

AndroidManifest.xml 里:

<application
        ...
        android:windowContentTransitions="true"/>

2. 創建Transition

方式一:通過XML創建

  1. 創建 res/transition/details_window_return_transition.xml 文件:
    <?xml version="1.0" encoding="utf-8"?>
    <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
                   android:transitionOrdering="together"
                   android:duration="500">
        <fade>
            <targets>
                <target android:excludeId="@android:id/statusBarBackground"/>
                <target android:excludeId="@android:id/navigationBarBackground"/>
            </targets>
        </fade>
        <slide android:slideEdge="top">
            <targets>
                <target android:targetId="@id/details_header_container"/>
            </targets>
        </slide>
        <slide android:slideEdge="bottom">
            <targets>
                <target android:targetId="@id/details_text_container"/>
            </targets>
        </slide>
    </transitionSet>
    
  2. 在代碼中載入 XML 文件定義的Transition
    TransitionInflater transitionInflater = TransitionInflater.from(this);
    Transition transition = transitionInflater.inflateTransition(R.transition.details_window_return_transition);
    

方式二:通過代碼創建

Explode explode = new Explode();
explode.addTarget(android.R.id.statusBarBackground);
explode.excludeTarget(android.R.id.navigationBarBackground, true);
Fade fade = new Fade();
Slide slide = new Slide();
TransitionSet transitionSet = new TransitionSet()
        .setDuration(500)
        .addTransition(explode)
        .addTransition(slide)
        .addTransition(fade);

3. 設置Transition

代碼設置

設置方法

  • ActivityonCreate()onCreateView()中使用getWindow()設置動畫。
  • FragmentonCreate()onCreateView()中使用getActivity().getWindow()設置動畫。

動畫種類

  • setExitTransition():當 A 啟動 B 時,使 A 中的View退出場景的transition
  • setEnterTransition():當 A 啟動 B 時,使 B 中的View進入場景的transition
  • setReturnTransition():當 B 返回 A 時,使 B 中的View退出場景的transition
  • setReenterTransition():當 B 返回 A 時,使 A 中的View進入場景的transition

XML設置

AndroidManifest.xml 里:

<activity
        ...
        android:theme="@style/AppTheme.Details"/>

res/values/theme.xml 里:

<resources>
    <style name="AppTheme.Details" parent="android:Theme.Material.Light.NoActionBar">
        <item name="android:statusBarColor">@android:color/black</item>
        <item name="android:windowExitTransition">@transition/details_window_enter_transition</item>
        <item name="android:windowEnterTransition">@transition/...</item>
        <item name="android:windowReenterTransition">@transition/...</item>
        <item name="android:windowReturnTransition">@transition/...</item>
    </style>
</resources>

4. 針對ViewGroup處理

  • 默認情況下,無法將ViewGroup當做一個view來處理,需要在ViewGroup的對應 XML 文件中開啟TransitionGroup屬性。設置ViewGroup背景色屬性也有同樣的效果,即便背景是透明的。
  • 若一個Transition中包含了沒有開啟TransitionGroup屬性的ViewGrouptargetId,則此Transition不會運行。

5. 啟動Activity

startActivity(intent, ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), (Pair<View, String>[]) null).toBundle());

監聽Transition動畫

工具類:TransitionListenerAdapter

getWindow().getEnterTransition().addListener(new TransitionListenerAdapter() { });

常見問題

  • 如果沒有任何效果,檢查是否把 動畫種類 弄錯了。

共享元素轉場動畫

簡單方式

1. 為共享元素設置TransitionName

為兩個場景想要共享的View調用setTransitionName()為相同可標識的字符串。

view.setTransitionName(/* 可標識字符串 */);

2. 啟動Activity

ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), view, view.getTransitionName());
startActivity(intent, optionsCompat.toBundle());

常見問題

  • 狀態欄導航欄 顯示不正常:可以把 導航欄狀態欄 作為共享元素。
View statusBar = getActivity().findViewById(android.R.id.statusBarBackground);
View navigationBar = getActivity().findViewById(android.R.id.navigationBarBackground);
List<Pair<View, String>> pairs = new ArrayList<>();
if (statusBar != null) {
    pairs.add(Pair.create(statusBar, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME));
}
if (navigationBar != null) {
    pairs.add(Pair.create(navigationBar, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));
}
// noinspection unchecked
startActivity(intent, ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), pairs.toArray(new Pair[pairs.size()])).toBundle());

復雜方式

  • 以下場景需要使用復雜方式來實現共享元素動畫:
    • 當共享的View延遲加載時,如網絡請求、FragmentViewPager等場景,需要復雜方式來處理。
    • 共享的ViewA啟動BB返回A 發生了改變,導致不是同一個View時。

0. 執行流程

工具類:SharedElementTransitionHelper

1. 為共享元素設置TransitionName

為兩個場景想要共享的View設置setTransitionName()為相同可標識的字符串。

view.setTransitionName(/* 可標識字符串 */);

2. 手動設置共享元素

方法簡介

  • setEnterSharedElementCallback()
    • 設置的是 A->B 時, B 的動畫。
    • 同時也是 B->A 時, B 的動畫,雖然動畫是相反的,但是會自動做倒序處理,可以看 執行流程 。
    • BActivity,則是由Activity.makeSceneTransitionAnimation()觸發的。若 BFragment,則是在Fragmentattached()detached()觸發的。
  • setExitSharedElementCallback()
    • 設置的是 A->B 時, A 的動畫。
    • 同時也是 B->A 時, A 的動畫,雖然動畫是相反的,但是會自動做倒序處理,可以看 執行流程
    • AActivity,則是由Activity.makeSceneTransitionAnimation()觸發的。若 AFragment,則是在Fragmentattached()detached()觸發的。

步驟

  1. 定義SharedElementCallback對象。重寫SharedElementCallback.onMapSharedElements()
  2. Activity或者Fragment中調用setEnterSharedElementCallback()setExitSharedElementCallback(),重新設置SharedElementCallback對象以實現重新設置 共享元素 。

簡單示例

背景
  • A 、 B 均為Activity。
  • B 返回 A 時, 共享元素 發生了變化。
A.java

必須調用子ActivitysetResult()才會調用父ActivityonActivityReenter()。

// 在Activity.onActivityReenter()中設置
SharedElementTransitionHelper.setExitSharedElementCallbackOnce(this, new SharedElementCallback() {
    @Override
    public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
        View newSharedElement = /* 獲取新共享元素 */;
        String newTransitionName = /* 獲取新TransitionName */;
        names.clear();
        sharedElements.clear();
        names.add(newTransitionName);
        sharedElements.put(newTransitionName, newSharedElement);
    }
});
B.java

此處不要在Activity.supportFinishAfterTransition()中設置,而應該在Activity.finishAfterTransition()。

// 在Activity.finishAfterTransition()中設置
// 必須調用setResult()才會調用父Activity的onActivityReenter()
setResult(RESULT_OK);
SharedElementTransitionHelper.setEnterSharedElementCallbackOnce(this, new SharedElementCallback() {
    @Override
    public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
        View newSharedElement = /* 獲取新共享元素 */;
        String newTransitionName = /* 獲取新TransitionName */;
        names.clear();
        sharedElements.clear();
        names.add(newTransitionName);
        sharedElements.put(newTransitionName, newSharedElement);
    }
});

4. 延遲和開始共享元素動畫

  • 用于處理在 轉場動畫 開始時, 共享元素 尚未加載的情況,此時延遲 轉場動畫 ,直至 共享元素 加載完畢。當有多個 共享元素 的時候也要確保所有這些 共享元素 全部加載完畢。
  • 在場景剛剛開始的地方,如Activity.onCreate()
    SharedElementTransitionHelper.pauseEnterTranstion(/* Activity */);
    
  • 在能訪問到 共享元素共享元素 加載完畢的地方,如SharedElementTransitionHelper中監聽了ViewPreDraw 階段:
    SharedElementTransitionHelper.startEnterTranstionWhenViewIsReady(getActivity(), /* 共享元素 */);
    

5. 啟動Activity

ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), view, view.getTransitionName());
startActivity(intent, optionsCompat.toBundle());

監聽共享元素動畫

getWindow().getSharedElementExitTransition().addListener(/* SharedElementTransitionHelper#TransitionListenerAdapter */);
getWindow().getSharedElementEnterTransition().addListener(/* SharedElementTransitionHelper#TransitionListenerAdapter */);

重疊屬性

控制兩個場景是否允許重疊。

代碼

getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);

效果

自定義共享元素動畫【Todo】

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <explode/>
    <changeBounds/>
    <changeTransform/>
    <changeClipBounds/>
    <changeImageTransform/>
</transitionSet>
  • 路徑:changeBounds
  • 大小、縮放:changeTransform
  • 圖片矩陣變換:ChangeImageTransform
  • 裁剪區域:ChangeClipBounds

教程

Material-Animations
Android Transition Framework
Postponed Shared Element Transitions

舊版轉場動畫【Todo】

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容