Android轉場動畫深度解析(3)

大頭呆發表於2017-09-11

終於到了material design轉場動畫中最好玩,最有特色的一部分了。我們沿用上一篇的圖,不過將跳轉Activity的程式碼作如下更改:

 Intent intent = new Intent(this,BBBActivity.class);
ActivityOptionsCompat activityOptionsCompat =ActivityOptionsCompat.makeSceneTransitionAnimation(this
                , new Pair<View, String>(shared_image, "shared_image_")
                , new Pair<View, String>(shared_text, "shared_text_"));
startActivity(intent, activityOptionsCompat.toBundle());複製程式碼

然後在BBBActivity的佈局檔案想要設定共享元素的部分設定android:transitionName,值和上個頁面中設定的值要一一對應,比如:

 <TextView
            android:id="@+id/tv_show"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="我是一行文字"
            android:transitionName="shared_text_"/>複製程式碼

當然也可以在程式碼中設定(注意要在呼叫時機,不能晚於OnResume):
shared_image.setTransitionName("shared_image_");

這樣簡單兩步,我們們的帶共享元素的轉場動畫就改造完成了:

自定義共享元素動畫

自帶的共享元素動畫很簡單,可以通過如下程式碼定義進入和返回動畫:

 getWindow().setSharedElementEnterTransition(Transition transition) 
 getWindow().setSharedElementReturnTransition(Transition transition)複製程式碼

仔細一看這兩個方法都只需要一個transition作為動畫,所以意味著自定義共享元素動畫就是自定義Transition了。套用第一篇自定義的那個直角移動ChangeRect,效果如下:

其他方法

上面我們只用兩個方法就完成了一次完整的共享元素進入到返回動畫。但其實和普通的轉場動畫一樣,設定共享元素的轉場動畫有四個,除了上面介紹的還有兩個就是

setSharedElementExitTransition
setSharedElementReenterTransition複製程式碼

看命名方式和普通的轉場動畫非常相似,也就是共享元素離開和重現動畫的方法。但是共享元素轉場是為了表現兩個頁面相似內容連貫性而設計的,一組動畫就足以完成了。但如果我們都加上後會怎麼樣呢?為了動畫更明顯,我們把普通動畫設為序列:


可以看到新增的兩個並沒有生效,通過日誌列印也可以卡出這一點:

SharedElementExitTransition和SharedElementReenterTransition開始後立即就結束了。關於這一點,參看stackoverflow上的回答,簡單來說這兩個動畫的設計只是為了作一些初始化而存在的。當我們點選跳轉按鈕的時候,馬上就已經跳到了B(參看上一篇生命週期的分析),而共享元素動畫沒有所謂的序列機制,會馬上執行SharedElementEnterTransition,所以轉場動畫內部會立即結束掉ExitTransition。而ReenterTransition我們也可以從gif圖看到,SharedElementReturnTransition已經完成了動畫,將目標View變為目標狀態,所以不再進行SharedElementReturnTransition(也因為沒必要),所以只進行了普通轉場動畫的ReenterTransition。

共享元素執行空間

Window中有個關於共享元素的設定setSharedElementsUseOverlay(boolean sharedElementsUseOverlay),我們將其設為false,重啟App:


可以看到動畫執行流程沒有變但是共享元素在移動過程中被遮住了,我們來看原始碼

  protected void moveSharedElementsToOverlay() {
        if (mWindow == null || !mWindow.getSharedElementsUseOverlay()) {
            return;
        }
        setSharedElementMatrices();
        int numSharedElements = mSharedElements.size();
        ViewGroup decor = getDecor();
        if (decor != null) {
            boolean moveWithParent = moveSharedElementWithParent();
            Matrix tempMatrix = new Matrix();
            for (int i = 0; i < numSharedElements; i++) {
                View view = mSharedElements.get(i);
                tempMatrix.reset();
                mSharedElementParentMatrices.get(i).invert(tempMatrix);
                GhostView.addGhost(view, decor, tempMatrix);
                ViewGroup parent = (ViewGroup) view.getParent();
                if (moveWithParent && !isInTransitionGroup(parent, decor)) {
                    GhostViewListeners listener = new GhostViewListeners(view, parent, decor);
                    parent.getViewTreeObserver().addOnPreDrawListener(listener);
                    mGhostViewListeners.add(listener);
                }
            }
        }
    }複製程式碼

可以看到,如果getSharedElementsUseOverlay==true(也就是預設狀態),系統會得到這個View,然後GhostView.addGhost(view, decor, tempMatrix),放置在decorView的Overlay上,因為是decorView,所以也就是在整個view樹結構的最上層。Overlay它是view的最上面的一個透明的層,新增到上面的和view不會被其他View遮擋住。

擴充:共享元素形變動畫

前面的動畫效果都是操作View原有的一些屬性,View的內容沒有(或者內容沒有改變),所以如果與Svg向量動畫配合一番,會產生怎麼樣的效果呢:


其實很簡單,監聽SharedElementEnterTransition和SharedElementReturnTransition動畫,在其執行的時候執行向量動畫就行了。在更多關於這個向量動畫實現的東西可以參考這篇文章

寫在最後

好了關於Android轉場動畫的內容完結了,程式碼已上傳gitHub,歡迎指正!

相關文章