Andrew
Andrew

Reputation: 4702

Android: How to use viewbinding in base class and migrate from kotlin synthetics

With the deprecation of kotlin synthetics I faced the problem, that I can not access some standard layouts from my base class anymore. For example, I have a base class for three fragments and in all of them, there is a button. My old approach was to get the button in the base class with synthetics and then assign some default clicklistener etc.

My question is: What would be the best approach to migrate from the synthetics to viewbinding / databinding here? Another question I am asking my self is, how could I access the view_layouts outside the activity / fragment now?

Base class

abstract class BaseRebuildFragment(layout: Int) : Fragment(layout) {
    abstract val nextFragment: NavDirections
    abstract val baseViewModel: ViewModel
    open val dataOverViewFragment: Boolean = false
    @Inject @RebuildProgressDescription lateinit var progressBarDescription: ArrayList<String>

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initAppStandardToolbar()
        initStateProgressBar(progressBarDescription)
        initButton()
        hideBottomNav()
    }

    private fun initButton() {
        calibrate_btn_next.setOnClickListener { // not working anymore
            if (dataOverViewFragment) return@setOnClickListener else if (this.validForm()) findNavController().navigate(nextFragment)
        }
    }

    open fun validForm(): Boolean { return false }
}

Fragment

@AndroidEntryPoint
class RebuildOptionFragment : BaseRebuildFragment(R.layout.fragment_rebuild_option) {
    override val baseViewModel: RebuildViewModel by navGraphViewModels(R.id.nav_send_rebuild) { defaultViewModelProviderFactory }
    private val rebuildBinding: FragmentRebuildOptionBinding by viewBinding()
    override val nextFragment: NavDirections = RebuildOptionFragmentDirections.actionRebuildOptionFragmentToRebuildUserDataFragment()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        bindObjects()
        baseViewModel.setStateEvent(RebuildStateEvent.GetPrice)
        subscribeRebuildListObserver()
    }

    private fun bindObjects() = with(rebuildBinding) {
        viewModel = baseViewModel
        lifecycleOwner = viewLifecycleOwner
        lendingListener = LendingSwitch()
    }

    override fun validForm(): Boolean = baseViewModel.shippingValidator.isShippingOptionsValid()
}

Accessing view outside of activity / fragment in utils package

fun Fragment.initStateProgressBar(progressBarDescription: ArrayList<String>) = with(app_standard_progress_bar) { // not working anymore
    setStateDescriptionData(progressBarDescription)
}

fun Fragment.initAppStandardToolbar() {
    toolbar.setupWithNavController(findNavController(), AppBarConfiguration(findNavController().graph)) // not working anymore
}

Upvotes: 1

Views: 1025

Answers (3)

EpicPandaForce
EpicPandaForce

Reputation: 81539

For dynamic views accessed without any certainty of type safety, you can use findViewById as you normally would have.

To access the view in a Fragment, you can use requireView().

Upvotes: 0

Tenfour04
Tenfour04

Reputation: 93551

findViewById can be used directly instead of using synthetics to accomplish the same thing, but without type safety. You have to provide the type since it won't be able to infer the type.

fun Fragment.initStateProgressBar(progressBarDescription: ArrayList<String>) = with(requireView().findViewById<ProgressBar>(R.id.app_standard_progress_bar)) { 
    setStateDescriptionData(progressBarDescription)
}

Upvotes: 1

cactustictacs
cactustictacs

Reputation: 19524

With view binding you're meant to inflate the layout yourself using the generated binding class - so for R.layout.fragment_rebuild_option it should be FragmentRebuildOptionBinding.

In onCreateView, call the inflate option on that class:

binding = FragmentRebuildOptionBinding.inflate(inflater, container, false)
// return the root layout view
return binding.root

That creates a binding object that has all your View references as properties, like you get with synthetics - so things like binding.calibrate_btn_next. That means binding needs to be a member variable in your Fragment class, so all your functions can access it.

Then it's basically just a case of changing all your code to refer to binding.whatever instead of just whatever

Upvotes: 1

Related Questions