My BaseFragment:
abstract class BaseFragment<ViewModel : BaseViewModel, Binding : ViewBinding> : Fragment() {
protected abstract val viewModel: ViewModel
private var _binding: Binding? = null
protected val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
_binding = Binding.inflate(inflater, container, false) //This line not working
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initialize()
setupListeners()
observe()
}
abstract fun initialize()
abstract fun setupListeners()
abstract fun observe()
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
But this line does not work _binding = Binding.inflate(inflater, container, false)
There is a working code but as for me this is shit coding:
abstract class BaseFragment<ViewModel : BaseViewModel, T : ViewBinding>() : Fragment() {
private var _binding:T? = null
protected abstract val viewModel: ViewModel
protected val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
val superclass: Type = javaClass.genericSuperclass!!
val aClass = (superclass as ParameterizedType).actualTypeArguments[1] as Class<*>
try {
val method: Method = aClass.getDeclaredMethod(
"inflate",
LayoutInflater::class.java,
ViewGroup::class.java,
Boolean::class.javaPrimitiveType
)
_binding = method.invoke(null, layoutInflater, container, false) as T
} catch (e: Exception) {
e.printStackTrace()
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initialize()
setupListeners()
observe()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
abstract fun initialize()
abstract fun setupListeners()
abstract fun observe()
}
How to do it right anybody has a clean solution. I know the DataBinding has DataBindingUtil but i need for ViewBinding. Is there something similar for ViewBinding
I hope for your answers
.
EDIT
Library: https://github.com/kirich1409/ViewBindingPropertyDelegate
BaseFragment:
abstract class BaseFragment<ViewModel : BaseViewModel, Binding : ViewBinding>(
layoutID: Int
) : Fragment(layoutID) {
protected abstract val viewModel: ViewModel
protected abstract val binding: Binding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupViews()
setupListeners()
setupObservers()
}
abstract fun setupViews()
abstract fun setupListeners()
abstract fun setupObservers()
}
class Fragment : BaseFragment<BaseViewModel, FragmentBinding>(
R.layout.fragment
) {
override val viewModel: BaseViewModel by viewModels()
override val binding: FragmentBinding by viewBinding() // this is from library
override fun setupViews() {
}
override fun setupListeners() {
}
override fun setupObservers() {
}
}
This is my baseViewBinding.
import android.content.pm.ActivityInfo
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
import java.lang.reflect.ParameterizedType
/**
* author:yxf
* time:2021/9/3
* BaseBindingFragment
*/
abstract class BaseBindingFragment<VB : ViewBinding> : Fragment() {
private var _binding : VB? = null
val binding :VB get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val type = javaClass.genericSuperclass
val clazz = (type as ParameterizedType).actualTypeArguments[0] as Class<VB>
val method = clazz.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java)
_binding = method.invoke(null, layoutInflater, container, false) as VB
return _binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
init()
}
abstract fun init()
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
}
To use
class HomeFragment : BaseBindingFragment<FragmentHomeBinding>() {
override fun init() {
//TODO() you can use binding in the whole Fragment
}
}
abstract class BaseFragment<DB : ViewDataBinding> : Fragment(), ViewContract<DB>, BaseConstant {
private var isRegistered = false
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if (getLayoutResourceId() != 0) {
val binding: DB = DataBindingUtil.inflate(inflater, getLayoutResourceId(), container, false)
binding.setLifecycleOwner { lifecycle}
onBindData(binding)
return binding.root
} else {
throw IllegalArgumentException("layout resource cannot be null")
}
}
override fun onResume() {
super.onResume()
if (!isRegistered) {
kotlinBus.register(this)
isRegistered = true
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
onInitLiveData()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
initActionView()
}
open fun initView() {
//TODO init view such as adapter, linearLayoutManager
}
open fun initActionView() {
//TODO init action for view such as onClick
}
open fun onInitLiveData(){
}
override fun onDetach() {
super.onDetach()
if (isRegistered) {
kotlinBus.unregister(this)
isRegistered = false
}
}
override fun onBindData(binding: DB) {
//TODO binding data into layout
}
}
simple ViewContract interface :
interface ViewContract<DB> {
fun getLayoutResourceId(): Int
fun onBindData(binding: DB)
}
and your fragment class could be:
class SampleFragment : BaseFragment<FragmentSampleBinding>() {
private val viewModel by viewModel<HabitsProgressOverallViewModel>()
@ExperimentalCoroutinesApi
private val homeViewModel: HomeViewModel by sharedViewModel()
override fun getLayoutResourceId(): Int {
return R.layout.fragment_sample
}
override fun onBindData(binding: FragmentOverallHabitsCompletionBinding) {
super.onBindData(binding)
binding.viewModel = viewModel
}
}
BaseFragment.kt
typealias Inflate<T> = (LayoutInflater, ViewGroup?, Boolean) -> T
abstract class BaseFragment<VB: ViewBinding>(
private val inflate: Inflate<VB>
) : Fragment() {
private var _binding: VB? = null
val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = inflate.invoke(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
HomeFragment.kt
class HomeFragment() : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::inflate) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.homeText.text = "Hello view binding"
}
}