【问题标题】:How to inject viewModel in BaseFragment using Koin如何使用 Koin 在 BaseFragment 中注入 viewModel
【发布时间】:2019-06-15 05:48:04
【问题描述】:

我创建了一个抽象的BaseFragment 类,它将被其他具体的Fragment 类扩展。我想使用Koin 在我的BaseFragment 中注入ViewModel。这是我的 BaseFragment:

abstract class BaseFragment<out VM : BaseViewModel, DB : ViewDataBinding>(private val mViewModelClass: Class<VM>) : Fragment() {

    val viewModel: VM by viewModel()

    open lateinit var binding: DB

    fun init(inflater: LayoutInflater, container: ViewGroup) {
        binding = DataBindingUtil.inflate(inflater, getLayoutRes(), container, false)
    }

    open fun init() {}
    @LayoutRes
    abstract fun getLayoutRes(): Int

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View {
        init(inflater, container!!)
        init()
        super.onCreateView(inflater, container, savedInstanceState)
        return binding.root
    }

    open fun refresh() {}
} 

但我不能这样做。我正在使用2.0.1 版本的 Koin。

【问题讨论】:

  • 你得到什么错误?顺便说一句,如果containernull,您的代码可能会崩溃,我建议您在此处使用安全运算符以防万一。
  • 我收到Cannot use VM as reified type parameter. Use a class Instead @JavierMendonça
  • 是的,我在想这些方面的东西。我不确定 Koin 能否弄清楚 VM 是什么类型。在这种情况下,VM 应该被具体化,以便 Kotlin 可以推断类型,以便 Koin 可以工作,但你不能在类定义中使用 reified 但是职能。 mViewModelClass 是什么?不好用它做什么?
  • 恐怕在不知道VM 是什么类型的情况下,您将无法以这种方式一般地注入它。把那个代码取出到每个片段,就一行代码??????????‍♂️。不过,您可以这样做的数据绑定,只需注意container!!
  • 尽管我不确定以这种方式完成数据绑定是否值得,但您需要在使用它的每个片段中强制转换它。您可以在每个片段中执行此操作,为了使其更具表现力,您可以使用此扩展功能:inline fun &lt;reified VD : ViewDataBinding&gt; ViewGroup.bind(layoutId: Int, attachToRoot: Boolean = false): VD = DataBindingUtil.inflate(LayoutInflater.from(context), layoutId, this, attachToRoot)。那你就做view.bind&lt;YourBindingClass&gt;(R.layout.your_binding),很方便。

标签: android kotlin android-viewmodel koin


【解决方案1】:

在我的情况下,我有同样的情况。你也可以这样做:

在扩展 BaseFragment 时将 ViewModel 添加为抽象和设置值。

我的BaseFragment有:

abstract class BaseFragment<Binding : ViewDataBinding, ViewModel : BaseViewModel> : Fragment() {
    protected abstract val mViewModel: ViewModel
    protected lateinit var bindingObject: Binding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        bindingObject = DataBindingUtil.inflate(inflater, getLayoutResId(), container, false)
        return bindingObject.root
    }

     /**
       * Get layout resource id which inflate in onCreateView.
      */
     @LayoutRes
     abstract fun getLayoutResId(): Int

     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        doDataBinding()
    }

     /**
      * Do your other stuff in init after binding layout.
      */
      abstract fun init()

     private fun doDataBinding() {
       bindingObject.lifecycleOwner = viewLifecycleOwner // it is extra if you want to set life cycle owner in binding
       // Here your viewModel and binding variable imlementation 
       bindingObject.setVariable(BR.viewModel, mViewModel)  // In all layout the variable name should be "viewModel"
       bindingObject.executePendingBindings()
       init()
}

}

这是我实际的 Fragment 实现:

class FragmentComments : BaseFragment<FragmentCommentsBinding, FragmentCommentsVM>() {
// Here is the your viewmodel imlementation 
override val mViewModel: FragmentCommentsVM by viewModel() 

override fun getLayoutResId(): Int = [fragment layout id like "R.layout.fragment_com"]

override fun init() {
...
}

我希望这对你有帮助。如果需要更多帮助,请告诉我!

【讨论】:

  • 我喜欢这种方法
  • 很高兴为您提供帮助
  • @pRaNaY 这里的viewModel() 方法是什么?我应该自己实现吗?
  • @Usease :它来自 koin viewmodel dep。查看更多关于它的信息,你可以从哪里得到这个想法:doc.insert-koin.io/#/koin-android/viewmodel
  • @pRaNaY 我修复了viewModel 方法。但是现在,我遇到了BR 未解决的错误。 BR 这里是什么?
【解决方案2】:

我目前正在解决同样的问题,查看Koin的源代码,by viewModel()提供了kotlin Lazy

/**
 * Lazy get a viewModel instance
 *
 * @param qualifier - Koin BeanDefinition qualifier (if have several ViewModel beanDefinition of the same type)
 * @param parameters - parameters to pass to the BeanDefinition
 * @param clazz
 */
fun <T : ViewModel> LifecycleOwner.viewModel(
        clazz: KClass<T>,
        qualifier: Qualifier? = null,
        parameters: ParametersDefinition? = null
): Lazy<T> = lazy { getViewModel(clazz, qualifier, parameters) }

在初始化时会调用其他 LifecycleOwner 扩展方法,该方法执行 viewModel 实例的实际解析:

/**
 * Lazy getByClass a viewModel instance
 *
 * @param clazz - Class of the BeanDefinition to retrieve
 * @param qualifier - Koin BeanDefinition qualifier (if have several ViewModel beanDefinition of the same type)
 * @param parameters - parameters to pass to the BeanDefinition
 */
fun <T : ViewModel> LifecycleOwner.getViewModel(
        clazz: KClass<T>,
        qualifier: Qualifier? = null,
        parameters: ParametersDefinition? = null
): T {
    return getKoin().getViewModel(
            ViewModelParameters(
                    clazz,
                    this@getViewModel,
                    qualifier,
                    parameters = parameters
            )
    )
}

我没有尝试过,但可以肯定地说,如果我直接在我的 BaseFragment 中调用此方法,它应该以相同的方式工作,我的 BaseFragment 看起来像:

abstract class BaseFragment<VM : ViewModel> : Fragment() {

    lateinit var viewModel: VM
    abstract val viewModelClass: KClass<VM>

    override fun onCreate(savedInstanceState: Bundle?) {
        viewModel = getViewModel(clazz = viewModelClass)

        super.onCreate(savedInstanceState)
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-24
    • 2022-12-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多