今回は、ViewModelProvider.Factory を使って、コンストラクタ経由で ViewModel に値を渡す方法を解説します。
なお、ここに掲載しているソースコードは以下の環境で動作確認しています。
- Android Studio Chipmunk | 2021.2.1 Patch 1
- JDK 11.0.12
- Android Gradle Plugin 7.2.1
- Kotlin 1.7.0
- Gradle 7.4.2
- androidx.lifecycle 2.4.1
- androidx.activity 1.4.0
概要
通常、ViewModel のコンストラクタに独自の引数、すなわち Application や SavedStateHandle 以外の引数を追加することはできません。
なぜなら、ViewModel の生成はその利用者が直接コンストラクタを呼ぶのではなくて、ViewModel を管理する ViewModelProvider にて ViewModel の Factory を使って行われるからです。
よって、ViewModel のコンストラクタに引数を追加して、その引数ありのコンストラクタで ViewModel を生成するためには、カスタムの Factory を作成し、そのインスタンスを ViewModelProvider に設定する必要があります。
今回は、実際に ViewModelProvider.Factory を継承して ViewModel のカスタム Factory を作成しながら、ViewModelProvider.Factory を使って、コンストラクタ経由で ViewModel に値を渡す方法を解説します。
ライブラリのインポート
ViewModel を使用するために、アプリの build.gradle ファイルの dependencies
に lifecycle-viewmodel-ktx
を追加します。また、Activity で viewModels を使って ViewModel インスタンスを取得するため、activity-ktx
も追加します。
dependencies { implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1" implementation "androidx.activity:activity-ktx:1.4.0" }
ViewModelProvider.Factory を使って、ViewModel に値を渡す
今回は、以下のようなサービスクラスをコンストラクタ経由で ViewModel に渡すコードを作成します。
interface SampleService { fun getText(): String } class SampleServiceImpl : SampleService { override fun getText(): String = "Sample" }
まず、ViewModel から作成します。
class SimpleViewModel( private val service: SampleService ) : ViewModel() { /** テキストを取得する。 */ fun getText() = service.getText() class Factory( private val service: SampleService ) : ViewModelProvider.NewInstanceFactory() { @Suppress("unchecked_cast") override fun <T : ViewModel> create(modelClass: Class<T>) = SimpleViewModel(service) as T } }
ViewModel ではコンストラクタ経由でさきほどの SampleService
を受け取りたいため、コンストラクタ引数に SampleService
を追加します。
class SimpleViewModel( private val service: SampleService ) : ViewModel() { ︙ }
次に、ViewModel の Factory を作成します。
ViewModel の Factory では、ViewModelProvider.NewInstanceFactory を継承して、コンストラクタ引数に SampleService
を追加します。この ViewModelProvider.NewInstanceFactory は ViewModelProvider.Factory を継承元に持ち、引数なしのコンストラクタで ViewModel を生成する Factory です。
そして、create メソッドをオーバーライドします。このメソッドは ViewModel を生成するメソッドなので、SimpleViewModel
を生成して、それを返します。
class Factory( private val service: SampleService ) : ViewModelProvider.NewInstanceFactory() { @Suppress("unchecked_cast") override fun <T : ViewModel> create(modelClass: Class<T>) = SimpleViewModel(service) as T }
最後に、ViewModel の利用側を作成します。
今回は、Activity で viewModels を使って ViewModel を取得します。このとき、viewModels に ViewModel の Factory のインスタンスを返すラムダを渡します。こうすることで、ViewModelProvider は渡された Factory を使って、ViewModel を生成します。
class MainActivity : AppCompatActivity(R.layout.main_activity) { private val viewModel: SimpleViewModel by viewModels { SimpleViewModel.Factory(SampleServiceImpl()) } ︙ }
以上で、コンストラクタ経由で ViewModel に値を渡せるようになります。
関連記事
ViewModel の使用方法については、以下で解説しています。 tatsurotech.hatenablog.com