Hi, I created this Fragment Base class to remove B...
# android
m
Hi, I created this Fragment Base class to remove Boilerplate code when using Viewmodels and (optionally) Databinding (also requires Koin) The only thing that isn't optimal right now is that I have to pass both the ViewModel as a Generic parameter and as a class in the constructor, due to Koin So basically any class that extends this Base class looks like this:
Copy code
class HomeFragment : ViewModelFragment<HomeViewModel>(R.layout.fragment_home,HomeViewModel::class)
The method Koin uses to get the ViewModel looks like this:
Copy code
fun <T : ViewModel> SavedStateRegistryOwner.getViewModel(
    clazz: KClass<T>,
    qualifier: Qualifier? = null,
    parameters: ParametersDefinition? = null
): T {
    return getKoin().getViewModel(this, clazz, qualifier, parameters)
}
And at last, my Base class looks like this:
Copy code
// 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
}
Any idea how I could remove the class parameter? I am pretty sure that the Generic parameter is required because otherwise (e.g. when saying)
Copy code
open lateinit var viewModel: ViewModel
instead of
Copy code
open lateinit var viewModel: VM
The inheriting class doesn't get access to methods and fields that It's viewmodel has, only to stuff that the ViewModel class has in general Additionally, is there a better way to get the specific binding than to use the method that I created? I know it's possible to accept the Binding as a Generic parameter as well but that also seems unnecessary. Thanks for your help.