
こんにちは、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でも安全にパラメータを扱えるようになりました。