この記事は?
- RecyclerView(一覧画面) -> 詳細画面 の構成
- SharedElementTransition の適用
- Fragment -> Fragment 遷移をNavigation Architecture Componentで行う
利用SDK
project/build.gradle
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.1.0-alpha02"
app/build.gradle
apply plugin: 'androidx.navigation.safeargs.kotlin'
// 省略
implementation "androidx.navigation:navigation-fragment-ktx:2.1.0-alpha02"
手順まとめ
一覧画面
-
postponeEnterTransition()
を実行しておく - RecyclerViewの描画直前に、
startPostponedEnterTransition()
を実行 - 共有したいViewに一意の
TransitionName
を設定- RecyclerView内のアイテムなら、position等を利用して一意にする
- 詳細画面に
TransitionName
を渡す - 遷移処理時に、
addSharedElement
をする
詳細画面
-
sharedElementEnterTransition
を設定する - 前画面と共有するViewに渡されてきた
TransitionName
を設定する
実装
Navigation Graph
xmlを記載したら、一度ビルドしましょう。
後ほど必要なクラスが生成されます。
nav_graph
<?xml version="1.0" encoding="utf-8"?>
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/home_fragment"
>
<!-- 省略 -->
<!-- 一覧画面 -->
<fragment
android:id="@+id/todo_list_fragment"
android:name="com.helmos.mysandbox.presentation.todo.TodoListFragment"
android:label="TodoListFragment"
tools:layout="@layout/fragment_todo_list"
>
<action
android:id="@+id/start_detail"
app:destination="@id/todo_detail_fragment"
>
<argument
android:name="todo"
app:argType="com.helmos.mysandbox.data.entity.Todo"
/>
<argument
android:name="transition_name"
app:argType="string"
/>
</action>
</fragment>
<!-- 詳細画面 -->
<fragment
android:id="@+id/todo_detail_fragment"
android:name="com.helmos.mysandbox.presentation.todo.detail.TodoDetailFragment"
android:label="TodoDetailFragment"
tools:layout="@layout/fragment_todo_detail"
>
<argument
android:name="todo"
app:argType="com.helmos.mysandbox.data.entity.Todo"
/>
<argument
android:name="transition_name"
app:argType="string"
/>
</fragment>
</navigation>
一覧画面
RecyclerViewの使い方などは省略しています。
TodoListFragment
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 省略
// RecyclerViewのTransitionは遅延させる
postponeEnterTransition()
todoRecycler.viewTreeObserver.addOnPreDrawListener {
startPostponedEnterTransition()
true
}
}
// onBindViewHolder 共有要素にTransitionNameを設定
view.transitionName = "todo_transition_$position"
// 一覧アイテムクリック時の処理
setOnItemClickListener { item, view ->
val todo = (item as TodoItem).todo
val sharedView = view.findViewById<View>(R.id.todo_icon)
// 画面遷移
findNavController().navigate(
// 自動生成されるDirectionsクラス
TodoListFragmentDirections.startDetail(todo, sharedView.transitionName),
// addSharedElementを行ってくれるUtilクラス
FragmentNavigatorExtras(
sharedView to sharedView.transitionName
)
)
}
詳細画面
TodoDetailFragment
// 自動生成される引数データクラス
private val args by navArgs<TodoDetailFragmentArgs>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 省略
// 共有要素に設定
todoImage.transitionName = args.transitionName
}
迷った点/今後調べること
android.R.transition.move
を使わないとダメだった
自作のtransitionだと、
Activity->Activity時には動いていたが、Fragment->Fragmentでは動作しなかった
何か制約があるのかも?
移動させるだけであれば、android.R.transition.move
でOK
詳細から戻った時にアニメーションしなかった
RecyclerView描画直前にstartPostponedEnterTransition()
を実行して解決
こちらもActivity->Activity時には設定していなくても動いていたので、Fragment間の遷移だと色々と勝手が違いそう??