如何使用 Dagger2 将 ViewModel 注入 BottomSheetDialogFragment?
How to inject a ViewModel into BottomSheetDialogFragment with Dagger2?
我使用 AndroidInjection.inject(this)
以首选方式将我的依赖项注入 Activity 和 Fragments 中。我有推荐的 ViewmodelFactory 来注入视图模型。我的注射在 Activities 和 Fragments 中起作用。但是我遇到了 BottomSheetDialogFragment 的问题,因为不允许我将 BottomSheetDialogFragment 指定为 this
。所以我的 @Inject lateinit var viewModelFactory: ViewModelFactory
没有初始化。我相信注入应该是可能的,因为 BottomSheetDialogFragment 应该是 Fragment class 的 subclass,但看起来不是。我使用 android.x 我相信这也会导致问题。难不成Dagger还不支持?
我应该用什么方式来实现我的 ViewModelFactory 注入?
更新:当我尝试使用 AndroidInjection.inject(this)
注入片段时,仅使用 android.app.Fragement
也不可能使用 androidx.fragment.app.Fragment
。我用 DaggerFragment
扩展了我的片段,它们按预期工作。
以下是我如何将 ViewModel 提供给 BottomSheetDialogFragment()。
首先在 MyApp class.
中设置 Dagger
class MyApp : Application(), HasActivityInjector, HasSupportFragmentInjector {
@Inject
lateinit var activityInjector: DispatchingAndroidInjector<Activity>
@Inject
lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment>
override fun onCreate() {
super.onCreate()
initDagger()
}
override fun activityInjector(): AndroidInjector<Activity> = activityInjector
override fun supportFragmentInjector(): AndroidInjector<Fragment> = supportFragmentInjector
private fun initDagger(){
DaggerAppComponent
.builder()
.application(this)
.build()
.injectApp(this)
}
然后是 AppComponent class
@Singleton
@Component(
modules = [
AppModule::class,
UiModule::class,
AndroidSupportInjectionModule::class,
AndroidInjectionModule::class
])interface AppComponent : AndroidInjector<MyApp> {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: MyApp): Builder
fun build(): AppComponent
}
fun injectApp(app: MyApp)
}
这里我们感兴趣UiModule::class
@Module
abstract class UiModule {
@Binds
abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
@PerFragment
@ContributesAndroidInjector(modules = [(FilterModule::class)])
abstract fun contributeFilterFragment(): FilterFragment
}
您已经拥有 ViewModelFactory ,所以我不粘贴此代码。
然后 FilterModule
@Module
abstract class FilterModule {
@Binds
@IntoMap
@PerFragment
@ViewModelKey(FilterViewModel::class)
abstract fun bindViewModel(viewModel: FilterViewModel): ViewModel
}
最后是 FilterFragment 和 FilterViewModel
class FilterFragment : BottomSheetDialogFragment() {
@Inject
lateinit var factory: ViewModelProvider.Factory
private lateinit var binding: FragmentFilterBinding
private lateinit var viewModel: FilterViewModel
override fun onAttach(context: Context?) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = getDataBinding(inflater, R.layout.fragment_filter, container)
viewModel = getViewModel(factory)
binding.viewModel = viewModel
return binding.root
}
}
class FilterViewModel @Inject constructor(private val testUseCase:TestUseCase) : BaseViewModel() {
//do something
}
fragment_filter布局
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.myapp.presentation.screen.filter.FilterViewModel" />
</data>
.......
getDataBinding() 和 getViewModel() 是扩展函数
fun <T : ViewDataBinding> Fragment.getDataBinding(inflater: LayoutInflater, @LayoutRes layoutId: Int, container: ViewGroup?): T =
DataBindingUtil.inflate(inflater, layoutId, container, false)
inline fun <reified T : BaseViewModel> Fragment.getViewModel(factory: ViewModelProvider.Factory = ViewModelProviders.DefaultFactory(activity!!.application)): T =
ViewModelProviders.of(this, factory).get(T::class.java)
我使用 AndroidInjection.inject(this)
以首选方式将我的依赖项注入 Activity 和 Fragments 中。我有推荐的 ViewmodelFactory 来注入视图模型。我的注射在 Activities 和 Fragments 中起作用。但是我遇到了 BottomSheetDialogFragment 的问题,因为不允许我将 BottomSheetDialogFragment 指定为 this
。所以我的 @Inject lateinit var viewModelFactory: ViewModelFactory
没有初始化。我相信注入应该是可能的,因为 BottomSheetDialogFragment 应该是 Fragment class 的 subclass,但看起来不是。我使用 android.x 我相信这也会导致问题。难不成Dagger还不支持?
我应该用什么方式来实现我的 ViewModelFactory 注入?
更新:当我尝试使用 AndroidInjection.inject(this)
注入片段时,仅使用 android.app.Fragement
也不可能使用 androidx.fragment.app.Fragment
。我用 DaggerFragment
扩展了我的片段,它们按预期工作。
以下是我如何将 ViewModel 提供给 BottomSheetDialogFragment()。 首先在 MyApp class.
中设置 Daggerclass MyApp : Application(), HasActivityInjector, HasSupportFragmentInjector {
@Inject
lateinit var activityInjector: DispatchingAndroidInjector<Activity>
@Inject
lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment>
override fun onCreate() {
super.onCreate()
initDagger()
}
override fun activityInjector(): AndroidInjector<Activity> = activityInjector
override fun supportFragmentInjector(): AndroidInjector<Fragment> = supportFragmentInjector
private fun initDagger(){
DaggerAppComponent
.builder()
.application(this)
.build()
.injectApp(this)
}
然后是 AppComponent class
@Singleton
@Component(
modules = [
AppModule::class,
UiModule::class,
AndroidSupportInjectionModule::class,
AndroidInjectionModule::class
])interface AppComponent : AndroidInjector<MyApp> {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: MyApp): Builder
fun build(): AppComponent
}
fun injectApp(app: MyApp)
}
这里我们感兴趣UiModule::class
@Module
abstract class UiModule {
@Binds
abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
@PerFragment
@ContributesAndroidInjector(modules = [(FilterModule::class)])
abstract fun contributeFilterFragment(): FilterFragment
}
您已经拥有 ViewModelFactory ,所以我不粘贴此代码。 然后 FilterModule
@Module
abstract class FilterModule {
@Binds
@IntoMap
@PerFragment
@ViewModelKey(FilterViewModel::class)
abstract fun bindViewModel(viewModel: FilterViewModel): ViewModel
}
最后是 FilterFragment 和 FilterViewModel
class FilterFragment : BottomSheetDialogFragment() {
@Inject
lateinit var factory: ViewModelProvider.Factory
private lateinit var binding: FragmentFilterBinding
private lateinit var viewModel: FilterViewModel
override fun onAttach(context: Context?) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = getDataBinding(inflater, R.layout.fragment_filter, container)
viewModel = getViewModel(factory)
binding.viewModel = viewModel
return binding.root
}
}
class FilterViewModel @Inject constructor(private val testUseCase:TestUseCase) : BaseViewModel() {
//do something
}
fragment_filter布局
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.myapp.presentation.screen.filter.FilterViewModel" />
</data>
.......
getDataBinding() 和 getViewModel() 是扩展函数
fun <T : ViewDataBinding> Fragment.getDataBinding(inflater: LayoutInflater, @LayoutRes layoutId: Int, container: ViewGroup?): T =
DataBindingUtil.inflate(inflater, layoutId, container, false)
inline fun <reified T : BaseViewModel> Fragment.getViewModel(factory: ViewModelProvider.Factory = ViewModelProviders.DefaultFactory(activity!!.application)): T =
ViewModelProviders.of(this, factory).get(T::class.java)