こんにちは、id:numanuma08です。ひさびさにAndroidネタです。
Navigationコンポーネントを使って画面遷移を定義しているとき、Fragment間でデータをやり取りするにはargument
で宣言したデータをby navArgs()
を使ってFragment上で利用できます。しかし、ViewModelで利用するときは一工夫必要でした。
AssistedInjectを使う方法がありましたが、AssistedInjectは@InstallIn(ViewModelComponent::class)
した依存関係を解決できません。
で、対策ですがSavedStateHandle
をViewModelに依存させます。SavedStateHandle.get
を使えばnavigationで定義した引数をViewModelでも利用できます。
しかし、SavedStateHandle.getは返り値がAny?
です。また引数にキーを文字列で指定しなければなりません。キーの定義や期待されるデータの型を誤るとクラッシュに繋がります。できればここは安全にやりたいところ。そう思ってドキュメントを眺めているとnavigationのアップデートでSavedStateHandleからパラメータを取り出すコードが自動生成する機能が追加されていました!また、逆にテスト用にパラメータからSavedStateHandleを生成するコードも自動生成されています。
これらのコードの使い方ですが非常に簡単です。
navigationを定義する
xmlでnavigationを定義し、引数を設定します。
<fragment android:id="@+id/home_fragment" android:name="net.numa08.ui.HomeFragment" android:label="home_fragment" tools:layout="@layout/home_fragment"> <argument android:name="id" android:defaultValue="0L" app:argType="long" /> </fragment>
ViewModelで次のようにパラメータを受け取ります。
@HiltViewModel class HomeViewModel @Inject constructor( savedStateHandle: SavedStateHandle ) : ViewModel() { private val args = HomeFragmentArgs.fromSavedStateHandle(savedStateHandle) // 自動生成されたfromSavedStateHandle private val id = args.id
このViewModelをテストするときは次のようにtoSavedStatehandle
を使います。
@Test fun test() { val args = HomeFragmentArgs(id = 100L).toSavedStateHandle() // 自動生成されたtoSavedStateHandle val viewModel = HomeViewModel(args)
まとめ
navigationのアップデートでViewModelでも安全にパラメータを扱えるようになりました。