您不能使用普通的泛型参数,例如内联函数中的具体化参数 (VM::class)。但是,如果您不想为每个片段编写by viewModels(),您可以使用肮脏的解决方法从其 Generic 类中实例化 viewModel。
但在我开始之前,值得一提的是viewModels<>() 是一个内联函数,它通过ViewModelProvider(store).get(vmClass) 懒惰地创建您的视图模型。因此,如果我们可以从参数化(通用)Fragment 类中提取 viewModel 的 Java 类,我们就可以使用它来获取我们的 viewModel。
在最简单的实现中,我们可以假设我们的片段中除了 BaseFragment 之外没有继承(这是 99% 的情况)。我们将得到genericSuperclass,它将在其actualTypeParameters 中表示实际的类型参数(我们正在寻找的ViewModel 类),然后我们使用第一个元素实例化viewModel
abstract class BaseFragment<VM : ViewModel> : Fragment() {
lateinit var viewModel: VM
private set
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// e.g. we are ProfileFragment<ProfileVM>, get my genericSuperclass which is BaseFragment<ProfileVM>
// Actually ParameterizedType will give us actual type parameters
val parameterizedType = javaClass.genericSuperclass as? ParameterizedType
// now get first actual class, which is the class of VM (ProfileVM in this case)
@Suppress("UNCHECKED_CAST")
val vmClass = parameterizedType?.actualTypeArguments?.getOrNull(0) as? Class<VM>?
if(vmClass != null)
viewModel = ViewModelProvider(this).get( vmClass )
else
Log.i("BaseFragment", "could not find VM class for $this")
}
}
class ProfileVM : ViewModel(){
var x = 1
}
class ProfileFragment : BaseFragment<ProfileVM>() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
Log.i("ProfileFragment", "vm.x: ${viewModel.x}")
return super.onCreateView(inflater, container, savedInstanceState)
}
}
此外,如果您想支持继承和复杂的层次结构,您可以使用 superClass 找到 BaseFragment,我将添加它作为另一个答案,因为我想保持这个答案干净整洁:D
PS:我不推荐您要查找的内容,因为如果您想创建一些只需要 sharedViewModel a.k.a. activityViewModel() 的片段,您必须为此添加一些更复杂的逻辑或处理对偶性手动创建一些视图模型,而这个神奇的代码将为您实例化其余的!