parentFragmentのスコープで生成するparentViewModels()
viewModels()とactivityViewModels()
Android ViewModelの生成は、activity-ktx, fragment-ktxで今はこう書くことができます。
val viewModel: MainViewModel by viewModels()
こう書くことでお手軽にViewModelを生成、そして管理することができるようになりました。まだ未生成の場合は生成して、画面回転やBackキーなどで戻ってきたときはViewModelProvider経由で取得してくれます。
例えばMainFragmentでviewModels()で生成したときは、このFragmentだけで有効なViewModelになります。
class MainFragment : Fragment() {
val mainViewModel: MainViewModel by viewModels()
}
class SubFragment : Fragment() {
// MainFragmentで生成したmainViewModelとは共有されてない
val mainViewModel: MainViewModel by viewModels()
}
ダイアログやBottomNavigation、ViewPager2などを使っていると、親のFragmentのViewModelを子のFragmentで共有して欲しい時があります。
その時のためにactivityViewModels()があります。
class MainFragment : Fragment() {
val mainViewModel: MainViewModel by activityViewModels()
}
class SubFragment : Fragment() {
// 同じActivityならシェアされる!
val mainViewModel: MainViewModel by activityViewModels()
}
これはViewModelの生存スコープがActivityになるので、同じActivity上のFragmentであればViewModelがシェアされるようになります。
activityViewModels()でシェアすることが問題になるケース
基本的にはこのactivityViewModels()でいい気がします。ですが、シングルActivityなどで実装されているプロジェクトの場合はスコープの範囲が広すぎてしまい、思わぬバグを引き起こしてしまいます。
例えば、いろいろな画面で使われるDialogのViewModelをシェアする時などです。このDialogのViewModelを呼び出しもとのFragmentで扱うためにactivityViewModels()を使うと、他の画面でこのDialogを呼び出した時に値が再発火してしまったりするからです。
そこで、呼び出しもとの親Fragmentと、呼び出されるDialogなどの子Fragmentと言うスコープ間でシェアされるviewModels()が必要になってきます。
parentViewModels()
parentViewModels()はデフォルトでは用意されてませんが、viewModels()のコードをちょっといじってparentFragmentで管理してくれる拡張を書いてみました。
このコードを適当にExtentions.ktなどに書いておきます。あとは通常のviewModels()と同じように使ってあげます。
MainFragmentで生成したViewModelをSubFragmentでも使いたい場合は下記のような感じで使います。
class MainFragment : Fragment() {
val mainViewModel: MainViewModel by viewModels()
}
class SubFragment : Fragment() {
// parentFragmentのスコープからMainViewModelを取得する
val mainViewModel: MainViewModel by parentViewModels()
}
この記事が気に入ったらサポートをしてみませんか?