這篇要來試試 Navigation 的 Shared Element Transition 的動態效果,以下如有解釋不清或是描述錯誤的地方還請大家多多指教:
在我們點擊列表的 item 進到 detail 時,通常都會設定一個過場的動態效果,像是 slide, popup 等等,今天要來試試共用元素的轉場效果,先來看看官方提供的範例:
要能達到以上的效果必須注意幾件事,以上面的 ImageView 作說明:
TransitionName
android:transitionName
每個元件都要是不同的 TransitionName,以城市名稱為例:
// item_city_card.xml
<TextView
android:id="@+id/cityName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:layout_marginTop="25dp"
android:layout_marginEnd="30dp"
android:textColor="@color/medium_gray"
android:textSize="45sp"
android:textStyle="bold"
android:transitionName="homeCity"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:text="LONDON" />
// fragment_detail.xml
<TextView
android:id="@+id/cityName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:layout_marginTop="25dp"
android:layout_marginEnd="30dp"
android:textColor="@color/medium_gray"
android:textSize="45sp"
android:textStyle="bold"
android:transitionName="detailCity"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:text="LONDON" />
我的列表 item 要去對應 detail 共享的元件的 TransitionName,並帶入 navigate()。
所以要先將 itemClick 的 interface 改造一下:
// HomeCityAdapter
...
class OnClickListener(val clickListener: (city: CityCard, extra: FragmentNavigator.Extras) -> Unit) {
fun onClick(city: CityCard, extra: FragmentNavigator.Extras) = clickListener(city, extra)
}
inner class CityViewHolder(private val binding: ItemCityCardBinding): RecyclerView.ViewHolder(binding.root) {
fun setupView(city: CityCard) = binding.apply {
val extras = FragmentNavigatorExtras(
cityCard to "detailBackground",
cityName to "detailCity",
weekName to "detailWeek",
cityTem to "detailTerm",
cityWindyIcon to "detailWindyIcon",
cityWindy to "detailWindy",
cityRainyIcon to "detailRainyIcon",
cityRainy to "detailRainy",
cityWetIcon to "detailWetIcon",
cityWet to "detailWet"
)
...
root.setOnClickListener {
onClickListener.onClick(city, extras)
}
}
}
...
Homefragment 接收的 click data 帶入 navigate():
private val cardAdapter by lazy { HomeCityAdapter(
HomeCityAdapter.OnClickListener { data, extra ->
// Safe Args
val action = HomeFragmentDirections.actionHomeFragmentToDetailFragment(data)
this.findNavController().navigate(action, extra)
}
)}
內頁則是要設定好 transition,設定 enter 及 return 的狀態:
// 使用預設的 transition
sharedElementEnterTransition = TransitionInflater.from(requireContext()).inflateTransition(android.R.transition.move)
sharedElementReturnTransition = TransitionInflater.from(requireContext()).inflateTransition(android.R.transition.move)
在 return Homefragment 時要將 transition 延後到資料準備好時執行:
private fun setupView() = binding.apply {
postponeEnterTransition()
...
cityList.apply {
adapter = cardAdapter
itemAnimator = null
doOnPreDraw { startPostponedEnterTransition() }
}
...
}