前言
??在 App 中,界面之間的切換是常有的。我們有時(shí)候還會(huì)碰到這樣的情況,在切換界面時(shí),切換前的界面和切換后的界面中有相同的組件。
??比如說,切換前的界面是一個(gè)列表界面,列表的每一項(xiàng)都含有標(biāo)題、內(nèi)容、圖片等信息,當(dāng)我們點(diǎn)擊列表中的一項(xiàng)時(shí),會(huì)調(diào)轉(zhuǎn)至詳情界面,詳情界面中也含有與列表項(xiàng)相同的標(biāo)題、內(nèi)容、圖片等信息。
??界面切換的邏輯很容易實(shí)現(xiàn)。但是我們想,是否有這樣的操作,讓列表項(xiàng)中的組件,平滑地 “移動(dòng)” 至切換后的界面,讓界面的切換不顯得那么生硬。SharedElement 可以幫助我們實(shí)現(xiàn)這樣的操作。
??先看一下普通的界面切換和使用 SharedElement 的界面切換兩者之間的區(qū)別。


??可以看出,兩者的差別很大,同樣是列表界面切換至詳情界面,普通的就是兩個(gè)界面之間生硬的切換,兩個(gè)界面各自獨(dú)立,而用 SharedElement 處理過后,給人的感覺就是在同一個(gè)界面中進(jìn)行操作(實(shí)質(zhì)還是兩個(gè)獨(dú)立的界面),整個(gè)交互給人的感覺,比前者好太多。
??下面介紹一下,如何使用 SharedElement 將原本生硬的界面切換 優(yōu)化成平滑的切換。
實(shí)現(xiàn)
??先來看一下邏輯,很簡單,就只有兩個(gè)界面,一個(gè)界面用于列表的展示,列表是用 RecyclerView 來實(shí)現(xiàn),RecyclerView 的 Item 被設(shè)置了點(diǎn)擊事件,當(dāng)列表項(xiàng)被點(diǎn)擊時(shí),會(huì)跳轉(zhuǎn)至另外一個(gè)詳情界面。

??直接上步驟。
??首先在 App 的主題中設(shè)置允許啟用窗口內(nèi)容轉(zhuǎn)換效果
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<!--允許啟用窗口內(nèi)容轉(zhuǎn)換效果-->
<item name="android:windowContentTransitions">true</item>
</style>
??在xml文件中,將兩個(gè)界面中相同視圖組件的定義都加上 android:transitionName 屬性(這邊是在列表 item 的 layout 和 詳情界面的 layout 中修改),要確保兩者的值是相等的,值是 string 類型,可以隨意定義,但是要保證同一個(gè) view 的值是相等 。
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="250dp"
android:scaleType="centerCrop"
android:transitionName="image"/>
??在列表的點(diǎn)擊事件中,常規(guī)的操作是創(chuàng)建一個(gè) Intent,加入一些參數(shù)后,調(diào)用 startActivity 去啟動(dòng)另一個(gè)界面。
Intent i = new Intent(mContext, DetailActivity.class);
i.putExtra("pos",position + "");
startActivity(i);
??若要實(shí)現(xiàn)平滑過渡, 則需要使用 ActivityOptionsCompat,調(diào)用它的 makeSceneTransitionAnimation 方法。傳入上下文 context,以及由 View 對象和我們之前聲明的 transitionName 構(gòu)建的 Pair 類型的對象(我這邊定義了三個(gè)Pair,實(shí)現(xiàn)三個(gè) view 的平滑切換,Pair 數(shù)量隨意,主要看需求),創(chuàng)建完 ActivityOptionsCompat 對象后,將其轉(zhuǎn)換成 Bundle 類型,與 Intent 一起作為參數(shù)傳給 startActivity 方法去啟動(dòng)另一個(gè)界面。
Intent i = new Intent(mContext, DetailActivity.class);
i.putExtra("pos",position + "");
android.support.v4.util.Pair<View, String> image = new android.support.v4.util.Pair(imageView, "image");
android.support.v4.util.Pair<View, String> text = new android.support.v4.util.Pair(textView, "text");
android.support.v4.util.Pair<View, String> longtext = new android.support.v4.util.Pair(textViewLong, "longtext");
ActivityOptionsCompat optionsCompat =
ActivityOptionsCompat.makeSceneTransitionAnimation((Activity) mContext, image,text,longtext);
startActivity(i,optionsCompat.toBundle());
??然后就沒有下一步了,是不是沒有想象中的麻煩,編譯運(yùn)行,來看一下效果。

??這樣,我們就把一個(gè)生硬的切換變成了一個(gè)平滑的切換,大大地增加了用戶感官上的體驗(yàn)。
還有
??sharedElement 從名字上來看是共享元素的意思,但是“元素”并沒有被共享,兩個(gè)界面依舊是獨(dú)立的不相干的,直白的講,sharedElement 可以理解成一種界面切換的動(dòng)畫,這個(gè)動(dòng)畫不用你自己去定義,你只要設(shè)定好同一個(gè)元素,之前的位置和之后的位置,位置變化的過程交由系統(tǒng)會(huì)幫你解決。
??還要注意的是,界面切換時(shí)被移動(dòng)的組件,盡量保持切換界面前和切換界面后的差別不要太大,比如說你之前 TextView 的字體大小為10sp,之后不要設(shè)置成30sp,顏色之類的也盡量保持一致,不然可能會(huì)出現(xiàn)閃屏現(xiàn)象。
??我把這個(gè) demo 放 GitHub 上了,感興趣的朋友可以了解一下 SharedElementDemo
以上。