iT邦幫忙

2022 iThome 鐵人賽

DAY 20
0
Mobile Development

【Kotlin Notes And JetPack】Build an App系列 第 20

Day 20.【Architecture】Navigation Shared Element Transition

  • 分享至 

  • xImage
  •  

這篇要來試試 Navigation 的 Shared Element Transition 的動態效果,以下如有解釋不清或是描述錯誤的地方還請大家多多指教:

什麼?

在我們點擊列表的 item 進到 detail 時,通常都會設定一個過場的動態效果,像是 slide, popup 等等,今天要來試試共用元素的轉場效果,先來看看官方提供的範例:

example
要能達到以上的效果必須注意幾件事,以上面的 ImageView 作說明:

  • 列表畫面的 ImageView 跟內頁的 ImageView 要定義不同的 TransitionName
  • 如果 app 最低支援是 API21 的話,可以直接在 XML 設定 android:transitionName
  • 在要進入的頁面要設定 transition resource

如何?

| 設定 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() }
    }
		...
}

| 最終呈現

final


上一篇
Day 19.【Architecture】Navigation 的介紹與應用
下一篇
Day 21.【Architecture】DataStore 的介紹與應用
系列文
【Kotlin Notes And JetPack】Build an App30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言