简体   繁体   中英

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
}

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)
}

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

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() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM