Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make BaseFragment with View Binding

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() {
        
    }
}
like image 408
Alis Abenov Avatar asked Nov 13 '20 10:11

Alis Abenov


People also ask

What is the difference between viewbinding and fragments?

But when it comes to ViewBinding with fragments the scenario changes. Because the lifecycle of the Fragment is different and that of activity is different Here also things go the same as discussed in the above article the naming conventions of the fragment layout are changed to pascal case and properties of the fragment layout to camel case.

Why use a base fragment?

Using a base fragment helps you avoid code and pattern repetition. You achieve a clean and readable code with the concepts discussed in this article. That is it for this article. Please share your thoughts on this subject.

What happens when requirebinding () is called outside of a fragment?

When requireBinding () is called outside of the Fragment’s view lifecycle, it will throw an IllegalStateException, similar to the behaviour of requireActivity (). This is the class that makes it possible, I added some comments to explain the details.

How to implement the UI of each fragment?

To implement the UI of each fragment you may refer to the following codes. Firstly the binding variable which is nullable is assigned to null initially, and also when the view of the fragment gets destroyed, again it has to be set null (which in this case _binding ).


1 Answers

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"
    }
}
like image 94
Jonny-sz Avatar answered Sep 21 '22 02:09

Jonny-sz