The Goal: Have a basic Android class that takes away the need to do common stuff in subclasses.
The Result: It works, but you have to inherit it like this:
class HomeFragment : ViewModelFragment(R.layout.fragment_home,HomeViewModel::class)
Is there a way to avoid passing the ViewModel once as a Generic parameter and once as a class?
The Generic is needed so var viewModel has access to specific stuff, otherwise (when just writing var viewModel:ViewModel it doesn’t have access to specific methods/fields like that of HomeViewModel)
The KClass is needed because of the getViewModel method provided by the Koin Library, which is also below.
// Created by Merthan Erdem
// We need to pass the generic class because only
// then will the subclass have access to the specific viewmodel methods that it
// We need to pass the actual class (right) because Koin can't use generic classes
abstract class ViewModelFragment<VM : ViewModel>(@LayoutRes val layout: Int, private val viewModelClass: KClass<VM>) : Fragment() { // Accessible, but generic, won't have specific methods/fields, see getSpecificBinding()
open var binding: ViewDataBinding? = null open lateinit var viewModel: VM override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Get binding (of passed type) for the passed layout, results in null when binding not available
binding = DataBindingUtil.inflate(inflater, layout, container, false)
// Set lifecycleowner to the current class
binding?.lifecycleOwner = this
// Get ViewModel of passed type from Koin (needs an actual class object/reified, not a generic)
viewModel = getViewModel(viewModelClass)
// If this class had a Binding, return it's root otherwise use inflater to retrieve view
return binding?.root ?: inflater.inflate(layout, container, false)
} override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.setVariable(BR.viewModel, viewModel)
} // Returns a binding of the specific type (instead of ViewDataBinding),
// which allows access to non-generic methods and fields
inline fun <reified specific : ViewDataBinding> getSpecificBinding() = binding as? specific
}
The getViewModel method
fun <T : ViewModel> SavedStateRegistryOwner.getViewModel(
clazz: KClass<T>,
qualifier: Qualifier? = null,
parameters: ParametersDefinition? = null
): T {
return getKoin().getViewModel(this, clazz, qualifier, parameters)
}
So, is there a way to prevent having to pass the ViewModel subclass twice? Reified doesn’t seem to work as this is a constructor. (Kotlin complains that the generic has to be reified when trying stuff like getViewModel(VM::class))
Thanks