Home > Software design >  Best practices for Fragments ViewBinding
Best practices for Fragments ViewBinding

Time:11-29

From a Google Codelab (can't remember which one), they adviced doing the following for fragments:

class MyFragment : Fragment() {

    private var _binding: MyFragmentBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View {
        _binding = MyFragmentBinding.inflate(inflater, container, false)
        
        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

}

And then accessing the views with e.g. binding.button1.

Is there a specific reason for doing it like this, with _binding and binding? Are there better methods? Perhaps an extension for Fragments - like a BaseFragment - to avoid code duplication.

CodePudding user response:

It's not recommended to use BaseFragment or BaseActivity or BaseViewModel... it will just add boilerplate code to your project.

For binding you can just use it like this:

Declaration:

private var binding: MyFragmentBinding? = null

onCreateView:

binding = MyFragmentBinding.inflate(inflater, container, false)
binding?.root

Usage:

binding?.button...
binding?.text...
binding?.cardView...

onDestroyView:

binding = null

And everything is going to work just fine but we use the null check a lot (?) and it's making the code messy and we need to get a lot of null checks if we need something from a certain view, so we are sure that between onCreateView and onDestroyView, the binding is not null so we have _binding and binding:

private var _binding: MyFragmentBinding? = null
private val binding get() = _binding!!

We make _binding mutable with var so we can give it a value, and we make it nullable so we can clear it later. And we have binding that have a custom getter so that means that each time we call binding it's going to return the latest value from _binding and force that it's not null with !!.

Now we seperate our variables, we have _binding to initialize and clear our binding, and we have binding that is immutable and not nullable to use it only for accessing views without the for null check ?

CodePudding user response:

See this question for some answers about the reason why binding needs to be nullable in a fragment.

See this answer of mine where I linked some articles about the problems with BaseFragments. You can usually achieve the code reuse without the drawbacks of inheritance by using extension properties and functions.

Here is an example of a property delegate that takes care of releasing the ViewBinding reference when necessary and rebuilding it when necessary. If you use this, all you need is a single binding property. Example is from the article about this tool.

class FirstFragment: Fragment(R.layout.first_fragment) {
  private val binding by viewBinding(FirstFragmentBinding::bind)

  override fun onViewCreated(view: View, bundle: Bundle?) {
    super.onViewCreated(view, bundle)

    binding.buttonPressMe.onClick {
      showToast("Hello binding!")
    }
  }
  • Related