如何将逻辑移动到 ViewModel?

How to move logic to ViewModel?

如何从我的 ViewModel 中的片段移动逻辑?

override fun onItemClick(titleName: Int) {
    when (titleName) {
        R.string.about_terms_service -> {
            activity?.addFragment(
                WebViewFragment.newInstance(
                    TERMS_LINK,
                    getString(R.string.about_terms_service)
                )
            )
        }
        R.string.about_open_source_licenses -> activity?.addFragment(LicensesFragment())
    }
}

此逻辑可以移至 ViewModel,但需要一些开销。当然,添加哪个片段的决定应该移到ViewModel,但是添加片段的代码应该留在Fragment(或Activity)。我认为它应该看起来像这样:

片段:

override fun onItemClick(titleName: Int) {
    viewModel.onTitleClick(titleName)
}

ViewModel:

fun onTitleClick(titleName: Int) {
    when (titleName) {
        R.string.about_terms_service -> {
            postViewModelEvent(ShowWebViewFragmentEvent())
        }
        R.string.about_open_source_licenses -> {
            // TODO: open License fragment
        }
    }
}

ViewModel 中,您应该将此 // TODOs 替换为针对您的视图的特定命令(FragmentActivity),这将触发导航到特定片段.例如,关于如何做到这一点的文章是 here(当然,任何 ViewModel - Fragment 解决方案都可以)。

在这种情况下,可以很容易地测试这个逻辑。

如果您通过视图模型事件(在 link 中描述)将 ViewModelFragment 连接,您可以这样做:

创建一个显示 WebViewFragment 的事件,如下所示:

class ShowWebViewFragmentEvent(): ViewModelEvent {
    override fun handle(activity: BaseActivity) {
        super.handle(activity)
        activity?.addFragment(
                WebViewFragment.newInstance(
                    TERMS_LINK,
                    getString(R.string.about_terms_service)
                )
            )
    }
}

和 post 它在你的 ViewModel 中(先替换 // TODO),像这样:

postViewModelEvent(ShowWebViewFragmentEvent())

请注意,linked post 中的所有必需更改都应该完成。

感谢 ,但我用下一个代码修改了这个解决方案:

片段:

override fun onItemClick(titleName: Int) {
    viewModel.onTitleClick(titleName)
}

视图模型:

fun onTitleClick(titleName: Int) {
        when (titleName) {
            R.string.about_terms_service -> {
                termsOfServiceItemClickEvent.postValue(
                    ViewModelEvent(
                        WebViewFragment.newBundle(
                            url = TERMS_LINK,
                            title = appContext.getString(R.string.about_terms_service)
                        )
                    )
                )
            }
            R.string.about_open_source_licenses -> licenseItemClickEvent.postValue(ViewModelEvent(R.string.about_open_source_licenses))
        }
    }

ViewModelEvent:

open class ViewModelEvent<out T>(private val content: T? = null) {

    var hasBeenHandled = false
        private set

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }
}

又是片段:

viewModel = ViewModelProviders
    .of(this, viewModelFactory)
    .get(AboutViewModel::class.java)

viewModel.licenseItemClickEvent.observe(this, Observer<ViewModelEvent<Int>> {
    it?.getContentIfNotHandled()?.let { activity?.addFragment(LicensesFragment()) }
})
viewModel.termsOfServiceItemClickEvent.observe(this, Observer<ViewModelEvent<Bundle>> {
    it?.getContentIfNotHandled()?.let { args ->
        activity?.addFragment(WebViewFragment.newInstance(args = args))
    }
})