Android Material Design Animation(二)


1 View transitions(View轉(zhuǎn)換效果)

1.1 什么是 Transition 溯革?

Transition 的中文翻譯是 “過(guò)渡”灾锯、“轉(zhuǎn)換”蟆淀、“轉(zhuǎn)變”鹦聪,這里對(duì)于 View Transition 個(gè)人粗淺的理解是:View 的場(chǎng)景切換他巨,即 View 的入場(chǎng)和出場(chǎng)充坑。

那么 View transition Aniamtion 就很好理解了,就是當(dāng) view 入場(chǎng)或出場(chǎng)時(shí)觸發(fā)的動(dòng)畫(huà)染突。

1.2 如何實(shí)現(xiàn) View Transition 動(dòng)畫(huà)

不管是自己實(shí)現(xiàn)捻爷,還是 Android 系統(tǒng)已經(jīng)幫我們實(shí)現(xiàn)了的機(jī)制,大概都能總結(jié)為 3 步:

  1. 捕獲view在開(kāi)始場(chǎng)景和結(jié)束場(chǎng)景中的狀態(tài)

  2. 基于view從一個(gè)場(chǎng)景到另一個(gè)場(chǎng)景的狀態(tài)改變份企,創(chuàng)建動(dòng)畫(huà)

  3. 執(zhí)行動(dòng)畫(huà)

1.3 Android Transition 框架是如何實(shí)現(xiàn)動(dòng)畫(huà)的呢也榄?

  1. beginDelayedTransition(), 傳入 viewFade,調(diào)用captureStartValues() 記錄場(chǎng)景中各個(gè) view 的可見(jiàn)性

  2. 設(shè)置 view 為不可見(jiàn)

  3. 在下一幀的時(shí)候司志,F(xiàn)ramework 調(diào)用 captureEndValues()甜紫,記錄場(chǎng)景中每個(gè) view 的可見(jiàn)性

  4. Framework 根據(jù)可見(jiàn)性發(fā)生變化的 view 的前后值,創(chuàng)建并返回 AnimatorSet

  5. Framework 運(yùn)行 Animator骂远,觸發(fā) view 逐漸進(jìn)入或退出場(chǎng)景

  6. 其中 Android 已經(jīng)預(yù)定義了 Fade, Slide, Explode

  7. 如果需要自定義動(dòng)畫(huà)的話(huà)囚霸,需要派生自 Visibility, 并重載 onAppearonDisappear,分別返回 View 入場(chǎng)和出場(chǎng)的動(dòng)畫(huà) Animator

  • 示例代碼如下:

    TransitionManager.beginDelayedTransition(mRootView, new Fade());
    toggleVisibility(mRedBox, mGreenBox, mBlueBox, mPurpleBox, mOrangeBox);
    
    private static void toggleVisibility(View... views) {
        for (View view : views) {
            boolean isVisible = view.getVisibility() == View.VISIBLE;
            view.setVisibility(isVisible ? View.INVISIBLE : View.VISIBLE);
        }
    }
    

    說(shuō)明:這里通過(guò)設(shè)置 view 的 visibility 來(lái)觸發(fā) view 的出場(chǎng)和入場(chǎng)

  • 效果如下:

image

說(shuō)明:紅色點(diǎn)擊對(duì)應(yīng) Fade, 綠色點(diǎn)擊對(duì)應(yīng) Slide, 藍(lán)色點(diǎn)擊對(duì)應(yīng) Explode激才,黃色點(diǎn)擊對(duì)應(yīng)自定義的圓形消失動(dòng)畫(huà)

1.4 基于 Transition Framework 創(chuàng)建動(dòng)畫(huà)的優(yōu)點(diǎn)

簡(jiǎn)單查看如下:

  1. 抽象了 Animator 的概念拓型,使用者并不需要知道底層是通過(guò) Animator 實(shí)現(xiàn)的

  2. 減少了代碼量,僅需要指定view的前后不同狀態(tài)瘸恼,Transition會(huì)自動(dòng)創(chuàng)建動(dòng)畫(huà)

  3. 有利于代碼復(fù)用劣挫,自定義的 View Transition Visibility 可以直接給其他模塊復(fù)用,且能使代碼結(jié)構(gòu)清晰

2 Activity Transitions(Activity 轉(zhuǎn)換效果)

2.1 版本區(qū)別

  • Android 5.0 之前

    可以通過(guò) Activity.overridePendingTransition();FragmentTransaction#setCustomAnimation(); 來(lái)實(shí)現(xiàn) Activity 之間的切換效果钞脂。

    示例代碼如下:

    activity.finish();
    overridePendingTransition(R.anim.anim_fade_in, R.anim.anim_fade_out);
    

    缺點(diǎn):只能對(duì)一個(gè) ActivityFragment 的整個(gè) view 做動(dòng)畫(huà)

  • Android 5.0

    • 允許對(duì)Activity/Fragment的單個(gè)view做動(dòng)畫(huà)
    • 允許定義共享view并創(chuàng)建動(dòng)畫(huà)

2.2 分類(lèi)

  • 按觸發(fā)時(shí)間劃分

    1. Exit transition (A啟動(dòng)B, A發(fā)生exit)

    2. Enter transition (A啟動(dòng)B, B發(fā)生enter)

    3. Return transition (B返回A, B發(fā)生return)

    4. Reenter transition (B返回A, A發(fā)生reenter)

  • 按內(nèi)容劃分

    1. Content Transition (內(nèi)容切換)

    2. Shared Element Transition (共享元素切換)

2.3 創(chuàng)建 Activity Transition 的步驟

  1. 在調(diào)用和被調(diào)用的 Activity 中揣云,通過(guò)設(shè)定 Window.FEATURE_ACTIVITY_TRANSITIONSWindow.FEATURE_CONTENT_TRANSITIONS 來(lái)啟動(dòng) transition api

    具體代碼如下:

    java 代碼中設(shè)置

    getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITION);
    

    style.xml 文件中設(shè)置

    <item name=“android:windowContentTransitions”>true</item>
    

    :使用 Material Design 主題的 app 默認(rèn)已啟動(dòng)

  2. 分別在調(diào)用與被調(diào)用的 Activity 中設(shè)置 exitenter transition冰啃。

    • Material 主題默認(rèn)會(huì)將 exittransition 設(shè)置為 null, 而 entertransition 設(shè)置成 Fade

    • 如果 reenterreturn transition 沒(méi)有明確設(shè)置邓夕,則將用 exitentertransition 替代

  3. 分別在調(diào)用與被調(diào)用的 Activity 中設(shè)置 exitenter 共享元素的 transition

    • Material 主題會(huì)默認(rèn)將 exit 的共享元素 transition 設(shè)置成 null阎毅,而 enter 的共享元素 transition 設(shè)置成 @android:transition/move

    • 如果 reenterreturn transition 沒(méi)有明確設(shè)置焚刚,則將用 exitenter 的共享元素 transition 替代

  4. 調(diào)用 startActivity(Context, Bundle) 啟動(dòng) Activity

    • 第二個(gè)參數(shù) ActivityOptions.makeSceneTransitionAnimation(activity, pairs).toBundle();

    • 其中 pairs 參數(shù)是一個(gè)數(shù)組:Pair<View, String>,該數(shù)組列出了 activity 中共享的 viewview 的名稱(chēng)

  5. 調(diào)用 finishAfterTransition() 觸發(fā)返回動(dòng)畫(huà)扇调,而不是 finish()

  6. 默認(rèn)情況下矿咕,Material 主題的 app 中 enter / returncontent transition 會(huì)在 exit / reentercontent transitions 結(jié)束之前開(kāi)始播放(只是稍微早于

    • 可調(diào)用 setWindowAllowEnterTransitionOverlap()setWindowAllowReturnTransitionOverlap() 方法屏蔽

2.4 創(chuàng)建 Fragment Transition 的步驟

  1. 頁(yè)面的 exit, enter, reenter, 和 return transition 需要調(diào)用 fragment 的相應(yīng)方法來(lái)設(shè)置,或者通過(guò)fragment 的 xml 屬性來(lái)設(shè)置。

  2. 共享元素的 enterreturn transition 也需要調(diào)用 fragment 的相應(yīng)方法來(lái)設(shè)置碳柱,或者通過(guò) fragment 的 xml 屬性來(lái)設(shè)置捡絮。

  3. 雖然在 Activity 中 transition 是被 startActivity()finishAfterTransition() 觸發(fā)的,但是 fragment 的 transition 卻是在其被 FragmentTransaction 執(zhí)行下列動(dòng)作的時(shí)候自動(dòng)發(fā)生的莲镣。added, removed, attached, detached, shown, hidden

  4. 在 fragment commit 之前福稳,共享元素需要通過(guò)調(diào)用 addSharedElement(View, String) 方法來(lái)成為 FragmentTransaction 的一部分

3 Acvitivity - Content Transitions

Content Transition 的概念:Activity 或 Fragment 切換的時(shí)候,定義非共享 view 進(jìn)入或離開(kāi)場(chǎng)景的方式(動(dòng)畫(huà))

相關(guān)的 api 有:setExitTransition(), setEnterTransition(), setReturnTransition(), setReenterTransition()

3.1 Content transition 如何觸發(fā)

假設(shè)從 Activity A 啟動(dòng) B瑞侮,則過(guò)程如下:

  • 當(dāng) Activity A 調(diào)用 startActivity

    1. Framework 遍歷 A 的場(chǎng)景樹(shù)的圆,并判斷哪些 view 會(huì)離開(kāi)場(chǎng)景

    2. A 的 exit transition 捕獲 transition view 的初始狀態(tài)

    3. Framework 設(shè)置全部的 transition viewINVISIBLE

    4. 在下一幀的時(shí)候,A 的 exit transition 捕獲 transition view 的終止?fàn)顟B(tài)

    5. A 的 exit transition 根據(jù)前后的狀態(tài)改變半火,創(chuàng)建 Animator 后并運(yùn)行

  • 當(dāng) Activity B 啟動(dòng)

    1. Framework 遍歷 B 的場(chǎng)景樹(shù)越妈,并判斷哪些 view 會(huì)進(jìn)入,將這些 view 初始化為 INVISIBLE

    2. B 的 enter transition 捕獲 transition view 的初始狀態(tài)

    3. Framework 設(shè)置全部的 transition viewVISIBLE

    4. 在下一幀的時(shí)候钮糖,B 的 enter transition 捕獲 transition view 的終止?fàn)顟B(tài)

    5. B 的 enter transition 根據(jù)前后的狀態(tài)改變梅掠,創(chuàng)建 Animator 后并運(yùn)行

3.2 演示效果 1

image

3.3 Content Transition 的對(duì)象

看到這里,相信大家已經(jīng)對(duì) Activity Content Transition 已經(jīng)有了比較清晰的理解了藐鹤。不過(guò)如果仔細(xì)查看演示效果瓤檐,發(fā)現(xiàn)還是有問(wèn)題,那就是頁(yè)面中的 WebView 并沒(méi)有做動(dòng)畫(huà)娱节。那么問(wèn)題來(lái)了:

  • 什么樣的對(duì)象可以當(dāng)做 Transition Objects

  • 能否把 ViewGroup 和它的 children 當(dāng)做一個(gè)整體做動(dòng)畫(huà)

查看源碼:

/** @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);
        }
    }
}

我們會(huì)發(fā)現(xiàn)如果接口 isTransitionGroup() 返回 true 的話(huà)挠蛉,那這個(gè) ViewViewGroup 就可以當(dāng)做一個(gè)整體來(lái)對(duì)待。然后再查看下 WebView.isTransitionGroup() 的原始返回值肄满,發(fā)現(xiàn)是 false谴古。到這里,我們就發(fā)現(xiàn)前面頁(yè)面中的 WebView 并沒(méi)有做動(dòng)畫(huà)的原因了稠歉,由于 Framework 捕獲 WebView 的時(shí)候掰担,isTransitionGroup() 返回 false, 由此開(kāi)始遍歷 WebView 的子控件,然后 getChildCount() 返回 0怒炸,那這里可以理解為 WebView 被忽略過(guò)去了带饱,導(dǎo)致我們?cè)O(shè)置的 Content Transition 動(dòng)畫(huà)無(wú)法在 WebView 上生效。我們只需調(diào)用 WebView.setTransitionGroup(true) 就能讓系統(tǒng)捕獲住 WebView阅羹。

3.4 演示效果 2

image

說(shuō)明:當(dāng)點(diǎn)擊最下面中間的按鈕之后勺疼,調(diào)用了 setTransitionGroup 之后,WebView 也開(kāi)始跟著做動(dòng)畫(huà)了

4 Acvitivity - Shared Element Transition

4.1 概念及 api

Shared Element Transition概念 :定義的共享 view捏鱼,在場(chǎng)景切換的時(shí)候执庐,觸發(fā)從場(chǎng)景1運(yùn)動(dòng)到場(chǎng)景2的對(duì)應(yīng)的位置的動(dòng)畫(huà)

相關(guān)的 api 有:

  • setSharedElementEnterTransition()

  • setSharedElementReturnTransition()

  • setSharedElementExitTransition()

  • setSharedElementReenterTransition()

4.2 Shared Element Transition 觸發(fā)過(guò)程

  1. 當(dāng) Activity A 啟動(dòng) B,B 被創(chuàng)建导梆,并且 B 的場(chǎng)景樹(shù)被 measured 和 laid out 在一個(gè)透明的窗口

  2. Framework 將 B 中的 shared element 放置在 A 對(duì)應(yīng)的位置上( resize和reposition )

  3. B enter transition 捕獲 shared element 在 B 上的終止?fàn)顟B(tài)

  4. B enter transition 比較 shared element 的初始和終止?fàn)顟B(tài)轨淌,創(chuàng)建 Animator

  5. Framework 隱藏 A 上的 shared element迂烁,并運(yùn)行 Animator

  6. 當(dāng) Animator 結(jié)束或者將要結(jié)束的時(shí)候,B 的窗口背景色逐漸轉(zhuǎn)為不透明递鹉,并觸發(fā) B 的 enter content transition

:默認(rèn)情況下盟步,在 transition 運(yùn)行過(guò)程中 shared element 在整個(gè)場(chǎng)景樹(shù)的頂層繪制□锝幔可以通過(guò)調(diào)用 Window#setSharedElementsUseOverlay(false) 來(lái)禁用默認(rèn)效果

4.3 Shared Element Transition 對(duì)象

Content Transition 不同的是址芯,Shared Element 需要主動(dòng)設(shè)置。

為了將上面的演示結(jié)果中懸浮按鈕指定為一個(gè) Shared Element窜觉,程序猿需要寫(xiě)如下代碼:

Transition1Activity.java

ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(
        Transition1Activity.this,
        Pair.create(mFabButton, "fab"));

Transition1Activity.xml

<Button
    android:id="@+id/fab_button"
    android:layout_width="@dimen/fab_big_size"
    android:layout_height="@dimen/fab_big_size"
    android:transitionName="fab" />

Transition2Activity.xml

<Button
    android:id="@+id/fab_button"
    android:layout_width="@dimen/fab_size"
    android:layout_height="@dimen/fab_big_size"
    android:transitionName="fab" />

說(shuō)明:對(duì)于 mFabButton 來(lái)說(shuō),需要在當(dāng)前 Activity 的 xml 文件中指定 transitionName北专,和后面跳轉(zhuǎn)的 Activity 的 xml 文件中也指定相同的 transitionName禀挫。在 java 代碼中,明確指定控件對(duì)象和設(shè)定的 transitionName拓颓。

4.4 Shared Element Transition 延遲啟動(dòng)

  • 為什么需要延遲啟動(dòng)

    1. 當(dāng) shared element 在 Fragment 中语婴,F(xiàn)ragmentTransactions 不會(huì)被立馬執(zhí)行

    2. 當(dāng) shared element 是大尺寸高分辨率的圖片時(shí),會(huì)觸發(fā)一次額外的排版

    3. 當(dāng) shared element 依賴(lài)于異步加載的數(shù)據(jù)時(shí)驶睦,導(dǎo)致排版不及時(shí)

  • 如何延遲啟動(dòng)

Android 給我們提供了 2 個(gè)接口 postponeEnterTransition()startPostponedEnterTransition()

具體使用代碼如下:

postponeEnterTransition();
mSharedElement.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw() {
        mSharedElement.getViewTreeObserver().removeOnPreDrawListener(this);
        mSharedElement.requestLayout();
        startPostponedEnterTransition();
        return true;
    }
});

4.5 演示效果

image

4.6 注意點(diǎn)

  1. 在調(diào)用 postponeEnterTransition 之后砰左,別忘記調(diào)用 startPostponeEnterTransition,忘記調(diào)用將導(dǎo)致 app 進(jìn)入一種死鎖狀態(tài)场航,無(wú)法進(jìn)入下一個(gè) activity/Fragment

  2. 延遲時(shí)間最好不要超過(guò)1秒鐘

參考資料

  1. http://www.androiddesignpatterns.com/2014/12/activity-fragment-transitions-in-android-lollipop-part1.html

  2. http://www.androiddesignpatterns.com/2014/12/activity-fragment-content-transitions-in-depth-part2.html

  3. http://www.androiddesignpatterns.com/2015/01/activity-fragment-shared-element-transitions-in-depth-part3a.html

  4. http://www.androiddesignpatterns.com/2015/03/activity-postponed-shared-element-transitions-part3b.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末缠导,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子溉痢,更是在濱河造成了極大的恐慌僻造,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孩饼,死亡現(xiàn)場(chǎng)離奇詭異髓削,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)镀娶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)立膛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人梯码,你說(shuō)我怎么就攤上這事宝泵。” “怎么了忍些?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵鲁猩,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我罢坝,道長(zhǎng)廓握,這世上最難降的妖魔是什么搅窿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮隙券,結(jié)果婚禮上男应,老公的妹妹穿的比我還像新娘。我一直安慰自己娱仔,他們只是感情好沐飘,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著牲迫,像睡著了一般耐朴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盹憎,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天筛峭,我揣著相機(jī)與錄音,去河邊找鬼陪每。 笑死影晓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的檩禾。 我是一名探鬼主播挂签,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼盼产!你這毒婦竟也來(lái)了饵婆?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤戏售,失蹤者是張志新(化名)和其女友劉穎啦辐,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蜈项,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芹关,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了紧卒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侥衬。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖跑芳,靈堂內(nèi)的尸體忽然破棺而出轴总,到底是詐尸還是另有隱情,我是刑警寧澤博个,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布怀樟,位于F島的核電站,受9級(jí)特大地震影響盆佣,放射性物質(zhì)發(fā)生泄漏往堡。R本人自食惡果不足惜械荷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望虑灰。 院中可真熱鬧吨瞎,春花似錦、人聲如沸穆咐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)对湃。三九已至崖叫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拍柒,已是汗流浹背归露。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斤儿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓恐锦,卻偏偏與公主長(zhǎng)得像往果,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子一铅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容