延時共享元素過渡 (part 3b)

延遲共享元素的過渡動畫 (part 3b)

>
* 原文鏈接 : Postponed Shared Element Transitions (part 3b)
* 譯者 : tiiime
* 校對者: chaossss

共享元素過渡延時 (part 3b)

這篇文章繼續我們關於共享元素 Transition 的深度分析,通過討論Lollipop Transition API
的一個重要的特性:延遲共享元素的過渡動畫。這是我關於這個話題的第四篇文章。



我們通過一個常見的問題來論述爲什麼需要推遲某些共享元素的過渡動畫。


理解問題

通常問題的根源是框架在 Activity 生命週期非常早的時候啓動共享元素 Transition 。回想我們的第一篇文章,
Transitions 必須捕獲目標 View 的起始和結束狀態來構建合適的動畫。因此,如果框架在共享元素獲得它在調用它的 App 所給定的大小和位置前啓動共享元素的過渡動畫,這個 Transition 將不能
正確捕獲到共享元素的結束狀態值,生成動畫也會失敗(一個過渡失敗的例子Video 3.3).


Your browser does not implement html5 video.


Transition 開始前,能否計算出正確的共享元素的結束值主要依靠兩個因素:(1) 調用共享元素 Activity
佈局的複雜度和層次結構的深度 (2)調用共享元素Activity載入數據消耗的時間。佈局越複雜,在屏幕上確定
共享元素的大小位置耗時越長。同樣,如果調用共享元素的 Activity 依賴一個異步的數據載入,框架
仍有可能會在數據載入完成前自動開始共享元素 Transition。下面列出的是你可能遇到的常見問題:


  • 共享元素存在於 Activity 託管的 Fragment 中 ( a Fragment hosted by the called
    activity)。

    FragmentTransactions 在commit後不會被立即執行;它們被安排到
    主線程等待執行。
    因此,如果共享元素存在的 Fragment 的視圖層和FragmentTransaction沒有被及時執行,框架有可能在
    共享元素被正確測量大小和佈局到屏幕前啓動共享元素 Transition。(1)


  • 共享元素是一個高分辨率的圖片。給 ImageView 設置一個超過其初始化邊界的高分辨率圖片,
    最終可能會導致在這個視圖層裏額外的佈局傳遞,由此增加在共享元素準備好前就
    啓動 Transition 的機率。流行的異步圖片處理庫比如 VolleyPicasso
    也不能可靠的解決這個問題:框架不能預先了解圖片是要被下載,縮放還是在後臺線程中從磁盤讀取,只是不管
    圖片是否處理完畢就啓動共享元素 Transition。

  • 共享元素依賴於異步的數據加載如果共享元素所需的數據是通過AsyncTask
    AsyncQueryHandler,Loader或者其他類似的東西加載,在它們最終返回數據前 Activity
    就能被確定,框架仍有可能在數據返回主線程前啓動 Transition

postponeEnterTransition() and startPostponedEnterTransition()

現在你可能會想:如果有辦法能讓暫時延遲 Transition 的使用,直到我們確定了共享元素的確切大小和
位置才使用它就好了。幸好
Activity Transitions API(2)爲我們提供瞭解決方案。

在 Activity 的onCreate()中調用postponeEnterTransition()
方法來暫時阻止啓動共享元素 Transition。之後,你需要在共享元素準備好後調用
startPostponedEnterTransition來恢復過渡效果。
常見的模式是在一個OnPreDrawListener中啓動延時 Transition,
它會在共享元素測量和佈局完畢後被調用(3)



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

    // Postpone the shared element enter transition.
    postponeEnterTransition();

    // TODO: Call the "scheduleStartPostponedTransition()" method
    // below when you know for certain that the shared element is
    // ready for the transition to begin.
}

/**
 * Schedules the shared element transition to be started immediately
 * after the shared element has been measured and laid out within the
 * activity's view hierarchy. Some common places where it might make
 * sense to call this method are:
 *
 * (1) Inside a Fragment's onCreateView() method (if the shared element
 *     lives inside a Fragment hosted by the called Activity).
 *
 * (2) Inside a Picasso Callback object (if you need to wait for Picasso to
 *     asynchronously load/scale a bitmap before the transition can begin).
 *
 * (3) Inside a LoaderCallback's onLoadFinished() method (if the shared
 *     element depends on data queried by a Loader).
 */
private void scheduleStartPostponedTransition(final View sharedElement) {
    sharedElement.getViewTreeObserver().addOnPreDrawListener(
        new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                sharedElement.getViewTreeObserver().removeOnPreDrawListener(this);
                startPostponedEnterTransition();
                return true;
            }
        });
}

忽略方法名,這裏還有第二種方法可以延遲共享元素返回 Transition,在調用Activity的onActivityReenter() 方法中延緩返回 Transition(4)

/**
 * Don't forget to call setResult(Activity.RESULT_OK) in the returning
 * activity or else this method won't be called!
 */
@Override
public void onActivityReenter(int resultCode, Intent data) {
    super.onActivityReenter(resultCode, data);

    // Postpone the shared element return transition.
    postponeEnterTransition();

    // TODO: Call the "scheduleStartPostponedTransition()" method
    // above when you know for certain that the shared element is
    // ready for the transition to begin.
}

儘管添加延時可以讓共享元素 Transition 更加流暢準確,但是你也要知道在應用中引入共享元素 Transition 的延遲還有一些副作用:

  • 調用postponeEnterTransition後不要忘記調用startPostponedEnterTransition
    忘記調用startPostponedEnterTransition會讓你的應用處於死鎖狀態,用戶無法進入下個Activity。

  • 不要將共享元素 Transition 延遲設置到1s以上。延遲時間過長會在應用中產生不必要的卡頓,影響用戶體驗。

感謝閱讀!希望這篇文章對你有所幫助。


1: 當然,許多應用通過調用FragmentManager#executePendingTransactions()來避開這個問題,這樣會強制立即執行FragmentTransactions而不是異步。

2: 注意!postponeEnterTransition()startPostponedEnterTransition()只對 Activity Transition起作用,對Fragment無效。詳細信息可以在這裏找到StackOverflow&Google+

3: 小貼士:你可以先調用View#isLayoutRequested()來確認是否需要調用OnPreDrawListener,有必要的話View#isLaidOut()在一些情況下也能派上用場

4: 在開發者選項中啓用不保留 Activity 選項可以方便調試共享元素的返回/重新進入過渡動畫行爲,這可以幫助測試返回的過渡效果開始之前最糟糕的情況( Activity 需要重新構造佈局加載數據…)

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