【问题标题】:What is the best way to handle Android ViewModels in navigation graph scope with a Fragment shared across multiple sub-graphs?使用跨多个子图共享的 Fragment 在导航图范围内处理 Android ViewModel 的最佳方法是什么?
【发布时间】:2021-08-22 23:22:51
【问题描述】:
可以从导航层次结构中的多个子图中使用一个片段是合理的。在这种情况下,如果 Fragment 依赖于父 Fragment 提供的视图模型,则视图模型需要位于根据其父级而更改的子图范围内。
Kotlin 提供了一种方便的方法来获取图形范围的视图模型:
private val fvm: SoftenerViewModel by navGraphViewModels(R.id.navigation_graph_softener)
但是这在子图 id 中是硬编码的。
解决这种情况的最佳方法是什么?
【问题讨论】:
标签:
android
androidx
android-viewmodel
android-navigation
【解决方案1】:
以下是一种方法,但鉴于新的扩展函数还没有在库中,它提出了一个问题,是否有更好的方法?
通过与 Kotlin 提供的扩展函数直接类比,定义以下扩展:
/**
* Derived from [androidx.navigation.navGraphViewModels]
*/
@MainThread
inline fun <reified VM : ViewModel> Fragment.navGraphViewModels(
viewModelArgKey: String,
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
): Lazy<VM> {
val backStackEntry by lazy {
val id = arguments?.getInt(viewModelArgKey, 0)?:0
require(id != 0) {"Fragment argument $viewModelArgKey required."}
findNavController().getBackStackEntry(id)
}
val storeProducer: () -> ViewModelStore = {
backStackEntry.viewModelStore
}
return createViewModelLazy(VM::class, storeProducer, {
factoryProducer?.invoke() ?: backStackEntry.defaultViewModelProviderFactory
})
}
这不同之处仅在于参数是一个字符串,而参数的名称是需要给出的。惰性确保片段参数在正确的时间被引用。
它就像现有的androidx库扩展一样使用:
private val fvm: SoftenerViewModel by navGraphViewModels("view_model_id")
唯一的要求是导航子图定义了参数:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mobile_navigation"
app:startDestination="@+id/navigation_home"
>
<fragment
android:id="@+id/navigation_home"
android:name="com...HomeFragment"
android:label="@string/title_home"
tools:layout="@layout/fragment_home"
/>
<navigation
android:id="@+id/navigation_graph_softener"
android:label="Softener"
app:startDestination="@id/navigation_softener"
>
<fragment
android:id="@+id/navigation_softener"
android:name="com.hanafey.android.waterstats.ui.softener.SoftenerFragment"
android:label="@string/title_softener"
tools:layout="@layout/fragment_softener"
/>
<fragment
android:id="@+id/navigation_softener_history"
android:name="com...SoftenerHistoryFragment"
android:label="Softener History"
tools:layout="@layout/fragment_softener_history"
>
<argument
android:name="view_model_id"
app:argType="reference"
android:defaultValue="@id/navigation_graph_softener"
/>
</fragment>
</navigation>
</navigation>