[Android] Navigation Architecture Component で、Shared Element Transition を使用した遷移方法
Navigation Architecture Component で、Shared Element Transition を使用した遷移方法
Android Architecture Components の Navigation Architecture Component で、Shared Element Transition を使用した遷移方法が、よくわからなんかったので調査してみました。
参考にしたコード
とりあえず、下記の2パターンで遷移できるとこまで作成
- 通常のViewをクリックしただけの遷移
- RecyclerViewで表示されている要素からの遷移
Navigation Component と Shared Elementsを使用した遷移
通常のNavigationComponentでの遷移時に、追加でFragmentNavigatorExtrasを使用してSharedElementsを指定します。
view.findViewById<View>(R.id.imageView).setOnClickListener {
it.findNavController().navigate(
TransitionSourceFragmentDirections.actionTransitionSourceFragmentToTransitionDestinationFragment(),
FragmentNavigatorExtras(
it to "testTransitionName"
)
)
}
遷移先では、通常のSharedElementsを使用した遷移と同様に,Viewに同じtransitionNameを指定しておき、
遷移時のアニメーションを指定します。
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(R.transition.move)
return inflater.inflate(R.layout.fragment_transition_destination, container, false)
}
これで、NavigationComponentを使用しながら、ShareadElementsも使用して遷移ができるようになります。
バックボタンで、前の画面に戻る際にも、アニメーションが入るようになります。
RecyclerViewを使用している場合の実装
RecyclerViewで選択した要素から、遷移する場合は上記だけでは、うまく動作しません。
追加で必要なポイントとしては、
* RecyclerViewの準備ができてから、遷移アニメーションを行うようにする
* 要素ごとにユニークなtransitionNameを使用する
になります。
まず、RecyclerViewの準備ができてから〜、の部分はviewTreeObserverを使用します。
いったんpostponeEnterTransitionでアニメーションを止めて、RecyclerViewの描画が終了してからアニメーションを開始させます。
これによって、バックボタンでRecyclerViewに戻った際にも、アニメーションが行われるようになります。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
postponeEnterTransition()
view.viewTreeObserver
.addOnPreDrawListener {
startPostponedEnterTransition()
true
}
}
次に、要素ごとにユニークなtransitionNameを使用するの部分は、
RecyclerView.Adapterで、各要素のViewをbindする際に、ユニークなtransitionNameをbindします。
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val item = mValues[position]
val transitionView = holder.itemView.findViewById<View>(R.id.imageView)
transitionView.transitionName = item.id
}
遷移時には、遷移先のViewにtransitionNameを渡すようにします。
これで、遷移先でも同じtransitionNameを使用できます。
override fun onClickItem(transitionView: View) {
view.findNavController().navigate(
TransitionSourceGridFragmentDirections.actionTransitionSourceGridFragmentToTransitionDestinationGridFragment(
transitionView.transitionName
),
FragmentNavigatorExtras(
transitionView to transitionView.transitionName
)
)
}
遷移先では、受け取ったtransitionNameを自身のViewにセットします。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
arguments?.let {
view.findViewById<View>(R.id.imageView).transitionName =
TransitionDestinationGridFragmentArgs.fromBundle(it).transitionName
}
}
まとめ
全体のコードは、ココにおいてあります。
やり方さえ分かれば、大した実装ではないです。
が、なかなかやり方がわからず、サンプルコードを見つけるまで苦労しました。