【问题标题】:Android: How to use viewbinding in base class and migrate from kotlin syntheticsAndroid:如何在基类中使用视图绑定并从 kotlin 合成迁移
【发布时间】:2020-12-07 21:19:19
【问题描述】:

随着kotlin synthetics 的弃用,我遇到了问题,我无法再从base class 访问一些标准布局。例如,我有一个用于三个片段的基类,并且在所有片段中都有一个按钮。我的旧方法是使用合成获取基类中的按钮,然后分配一些默认的 clicklistener 等。

我的问题是:从合成迁移到视图绑定/数据绑定的最佳方法是什么?我问自己的另一个问题是,我现在如何访问活动/片段之外的 view_layouts?

基类

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

片段

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

在 utils 包中访问活动/片段之外的视图

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
}

【问题讨论】:

    标签: android kotlin kotlin-android-extensions


    【解决方案1】:

    findViewById 可以直接使用,而不是使用合成来完成同样的事情,但没有类型安全。您必须提供类型,因为它无法推断类型。

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

    【讨论】:

    • 在使用这种方法时是否存在任何的性能劣势?它总是对我说Unresolved reference: FindViewById
    • 不适用于此用例,因为您只为活动或片段调用一次。无论如何,第一次调用合成属性时,它们都会在后台使用 findViewById。您可能希望避免在列表中重复使用它,例如在 RecyclerView 的 onBindViewHolder 中使用它,因为那样它可能会变得可感知。看上面更新的代码,我漏掉了什么。
    【解决方案2】:

    使用视图绑定,您可以使用生成的绑定类自行扩展布局 - 所以对于 R.layout.fragment_rebuild_option,它应该是 FragmentRebuildOptionBinding

    onCreateView 中,调用该类的inflate 选项:

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

    这将创建一个绑定对象,该对象将您的所有 View 引用作为属性,就像您使用合成一样 - 所以像 binding.calibrate_btn_next 这样的东西。这意味着binding 需要成为Fragment 类中的成员变量,以便您的所有函数都可以访问它。

    然后基本上只是将所有代码更改为引用binding.whatever 而不仅仅是whatever

    【讨论】:

    • OP 的挑战在于这个函数对所有片段都是通用的,每个片段都有不同的绑定类。
    • @Tenfour04 没错
    • 哦,我明白了,是的 - 我认为问题是合成几乎是动态类型的,所以你可以避免引用那些不能保证在你传入的布局中的东西. View Binding 更像是静态类型,具有强制执行的所有限制(还有更多 - 它们甚至没有在ViewBinding 接口中包含inflate!)您可能需要改用数据绑定,设置公共变量在布局中(如点击监听器)并动态设置:developer.android.com/topic/libraries/data-binding/…
    【解决方案3】:

    对于在没有任何类型安全确定性的情况下访问的动态视图,您可以像往常一样使用findViewById

    要访问片段中的视图,您可以使用requireView()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多