如何将ViewBinding与抽象基类一起使用

时间:2020-06-16 11:54:13

标签: android android-viewbinding

我开始使用ViewBinding。在搜索了一些示例或建议后,如何将ViewBinding与抽象基类一起使用,该抽象基类应处理预期在每个孩子的布局中出现的视图的相同逻辑,然后我在这里提出这个问题。

场景:
我有一个基类public abstract class BaseFragment。有多个Fragment扩展了这个基类。这些片段具有从基类实现处理的公共视图(使用“旧” findViewById())。例如,每个片段的布局都应包含ID为text_title的TextView。从BaseFragmentonViewCreated()的处理方式如下:

TextView title = view.findViewById(R.id.text_title);
// Do something with the view from the base class

现在,ViewBinding-API为每个子片段生成绑定类。我可以使用绑定引用视图。但是我不能使用基类中的具体绑定。即使将泛型引入基类中,我仍然放弃了太多类型的片段绑定。

从抽象基类处理绑定视图的推荐方法是什么?有什么最佳实践吗?没有在API中找到内置机制来以一种优雅的方式处理这种情况。

当期望子片段包含公共视图时,我可以提供抽象方法,这些方法从Fragment的具体绑定中返回视图,并使它们可从基类访问。 (例如protected abstract TextView getTitleView();)。但这是一个优势,而不是使用findViewById()吗?你怎么看?还有其他(更好的)解决方案吗?请让我们开始讨论。

9 个答案:

答案 0 :(得分:2)

我为我的具体情况找到了适用的解决方案,并且不想与您分享。

请注意,这并不是对ViewBinding的工作方式的解释。

我在下面创建了一些伪代码与您分享。 (从我的解决方案中使用显示DialogFragments的{​​{1}}迁移)。我希望它几乎完全适合片段(AlertDialogonCreateView())。我让它那样工作。

想象一下,我们有一个抽象onCreateDialog()和两个扩展类BaseFragmentFragmentA

首先看一下我们所有的布局。请注意,我将布局的可重用部分移到了一个单独的文件中,该文件稍后将包含在具体片段的布局中。特定视图保留在其片段的布局中。对于这种情况,使用常用的布局很重要。

fragment_a.xml:

FragmentB

fragment_b.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    
    <!-- FragmentA-specific views -->
    <EditText
        android:id="@+id/edit_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text" />
    
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/edit_name">

        <!-- Include the common layout -->
        <include
            layout="@layout/common_layout.xml"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </RelativeLayout>
</RelativeLayout>

common_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    
    <!-- FragmentB-specific, differs from FragmentA -->
    <TextView
        android:id="@+id/text_explain"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/explain" />
    
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/text_explain">

        <!-- Include the common layout -->
        <include
            layout="@layout/common_layout.xml"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </RelativeLayout>
</RelativeLayout>

接下来是片段类。首先是我们的<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:parentTag="android.widget.RelativeLayout"> <Button android:id="@+id/button_up" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/up"/> <Button android:id="@+id/button_down" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/button_up" android:text="@string/down" /> </merge> 实现。

BaseFragment是绑定膨胀的地方。我们能够根据包含onCreateView()的片段的绑定来绑定CommonLayoutBinding。我定义了一个抽象方法common_layout.xml,该方法在onCreateViewBinding()之上调用,该方法从onCreateView()VewBinding返回FragmentA。这样,我可以确保在需要创建FragmentB时存在片段的绑定。

接下来,我可以通过调用CommonLayoutBinding创建CommonLayoutBinding的实例。请注意,具体片段绑定的根视图将传递到commonBinding = CommonLayoutBinding.bind(binding.getRoot());

bind()允许从扩展的片段提供对getCommonBinding()的访问。我们可以更严格一些:CommonLayoutBinding应该提供访问该绑定的具体方法,而不是公开给子类。

BaseFragment

现在看看其中一个子类private CommonLayoutBinding commonBinding; // common_layout.xml @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // Make sure to create the concrete binding while it's required to // create the commonBinding from it ViewBinding binding = onCreateViewBinding(inflater); // We're using the concrete layout of the child class to create our // commonly used binding commonBinding = CommonLayoutBinding.bind(binding.getRoot()); // ... return binding.getRoot(); } // Makes shure to create the concrete binding class from child-classes before // the commonBinding can be bound @NonNull protected abstract ViewBinding onCreateViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container); // Allows child-classes to access the commonBinding to access common // used views protected CommonLayoutBinding getCommonBinding() { return commonBinding; } 。 与FragmentA一样,我们从onCreateViewBinding()创建绑定。在原则上,它仍从onCreateView()中调用。从如上所述的基类中使用此绑定。我正在使用onCreateVIew()来访问getCommonBinding()的视图。现在common_layout.xml的每个子类都可以从BaseFragment访问这些视图。

这样,我可以将基于通用视图的所有逻辑上移到基类。

ViewBinding

优点:

  • 通过将其移至基类来减少重复的代码。现在,所有片段中的代码都更加清晰了,并简化为基本内容
  • 通过将可重用的视图移动到private FragmentABinding binding; // fragment_a.xml @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // Make sure commonBinding is present before calling super.onCreateView() // (onCreateViewBinding() needs to deliver a result!) View view = super.onCreateView(inflater, container, savedInstanceState); binding.editName.setText("Test"); // ... CommonLayoutBinding commonBinding = getCommonBinding(); commonBinding.buttonUp.setOnClickListener(v -> { // Handle onClick-event... }); // ... return view; } // This comes from the base class and makes sure we have the required // binding-instance, see BaseFragment @Override protected ViewBinding onCreateViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { binding = FragmentABinding.inflate(inflater, container, false); return binding; } 所包含的布局中,可以使布局更加简洁

缺点:

  • 在视图无法移动到常用布局文件的地方可能并不完全适用
    • 可能需要在碎片/布局之间放置不同的视图
    • 许多<include />布局将导致产生许多Binding类,然后没有任何结果可赢得
  • 需要另一个绑定实例(<included />)。每个子级(CommonLayoutBindingFragmentA)不仅只有一个绑定类,它提供对视图层次结构中所有视图的访问权限

如果无法将视图移动到通用布局怎么办?
我对如何解决此最佳实践非常感兴趣!让我们考虑一下:在具体的FragmentB周围引入包装器类。我们可以引入一个接口,该接口提供对常用视图的访问。从片段中,我们将绑定包装在这些包装器类中。另一方面,这将导致每个ViewBinding-type的许多包装。但是我们可以使用抽象方法(泛型)将这些包装器提供给ViewBinding。然后BaseFragment可以使用定义的接口方法访问视图或对其进行处理。 您如何看待?

结论:
也许仅仅是一个布局需要拥有自己的Binding-Class的ViewBinding的实际限制。 如果在无法共享布局且需要在每个布局中声明重复的情况下找到了很好的解决方案,请告诉我。

我不知道这是最佳做法还是有更好的解决方案。但是,直到这是我的用例唯一已知的解决方案,它似乎才是一个好的开始!

答案 1 :(得分:2)

您好,我创建了一个博客文章,其中涵盖了视图绑定的深入内容,还包括构图模式/委托模式以实现视图绑定以及使用来自链接的继承签出

检出BaseActivityBaseFragment的完整代码以及用法

?Androidbites|ViewBinding

/*
 * In Activity
 * source : https://chetangupta.net/viewbinding/
 * Author : ChetanGupta.net
 */
abstract class ViewBindingActivity<VB : ViewBinding> : AppCompatActivity() {

    private var _binding: ViewBinding? = null
    abstract val bindingInflater: (LayoutInflater) -> VB

    @Suppress("UNCHECKED_CAST")
    protected val binding: VB
        get() = _binding as VB

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        _binding = bindLayout.invoke(layoutInflater)
        setContentView(requireNotNull(_binding).root)
        setup()
    }

    abstract fun setup()

    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }
}
/*
 * In Fragment
 * source : https://chetangupta.net/viewbinding/
 * Author : ChetanGupta.net
 */
abstract class ViewBindingFragment<VB : ViewBinding> : Fragment() {

    private var _binding: ViewBinding? = null
    abstract val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> VB

    @Suppress("UNCHECKED_CAST")
    protected val binding: VB
        get() = _binding as VB

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = bindLayout.invoke(inflater, container, false)
        return requireNotNull(_binding).root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setup()
    }

    abstract fun setup()

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

有关用法,请使用高级模式和反模式结帐博客Androidbites|ViewBinding

答案 2 :(得分:1)

以下是我的 BaseViewBindingFragment 的完整示例:

  • 不需要任何 abstract 属性或函数,
  • 它依赖于 Java 反射(不是 Kotlin 反射) - 参见 fun createBindingInstance,其中使用了 VB 泛型类型参数
package app.fragment

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
import java.lang.reflect.ParameterizedType

/**
 * Base application `Fragment` class with overridden [onCreateView] that inflates the view
 * based on the [VB] type argument and set the [binding] property.
 *
 * @param VB The type of the View Binding class.
 */
open class BaseViewBindingFragment<VB : ViewBinding> : Fragment() {

    /** The view binding instance. */
    protected var binding: VB? = null

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
        createBindingInstance(inflater, container).also { binding = it }.root

    override fun onDestroyView() {
        super.onDestroyView()

        binding = null
    }

    /** Creates new [VB] instance using reflection. */
    @Suppress("UNCHECKED_CAST")
    protected open fun createBindingInstance(inflater: LayoutInflater, container: ViewGroup?): VB {
        val vbType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
        val vbClass = vbType as Class<VB>
        val method = vbClass.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java)

        // Call VB.inflate(inflater, container, false) Java static method
        return method.invoke(null, inflater, container, false) as VB
    }
}

答案 3 :(得分:1)

基类会像这样

abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity(){

    protected lateinit var binding : VB

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = inflateLayout(layoutInflater)
        setContentView(binding.root)
    }

    abstract fun inflateLayout(layoutInflater: LayoutInflater) : VB
}

现在在您要使用的活动中

class MainActivity : BaseActivity<ActivityMainBinding>(){

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding.tvName.text="ankit"
    }

    override fun inflateLayout(layoutInflater: LayoutInflater)  = ActivityMainBinding.inflate(layoutInflater)
}

现在在 onCreate 中只需根据使用情况使用绑定

答案 4 :(得分:0)

我还提出了一个基类解决方案,该解决方案有效地使用了最终变量。我的主要目标是:

  1. 在基类中处理所有绑定生命周期
  2. 让子类提供绑定类实例,而无需自己使用该路由(例如,如果我有抽象函数abstract fun getBind():T,子类可以实现它并直接调用它。我不希望那样做)因为这样可以使绑定保持在基类中的全部意义,我相信)

就这样。首先是我的应用程序的当前结构。这些活动不会自行膨胀,基类会为它们完成:

儿童活动和片段:

class MainActivity : BaseActivityCurrent(){

    var i = 0

    override val contentView: Int
        get() = R.layout.main_activity


    override fun setup() {
        supportFragmentManager.beginTransaction()
            .replace(R.id.container, MainFragment())
            .commitNow()

        syntheticApproachActivity()
    }


    private fun syntheticApproachActivity() {
        btText?.setOnClickListener { tvText?.text = "The current click count is ${++i}"  }
    }


    private fun fidApproachActivity() {
        val bt = findViewById<Button>(R.id.btText)
        val tv = findViewById<TextView>(R.id.tvText)

        bt.setOnClickListener { tv.text = "The current click count is ${++i}"  }
    }
}

//-----------------------------------------------------------
class MainFragment : BaseFragmentCurrent() {
    override val contentView: Int
        get() = R.layout.main_fragment


    override fun setup() {
        syntheticsApproach()
    }

    private fun syntheticsApproach() {
        rbGroup?.setOnCheckedChangeListener{ _, id ->
            when(id){
                radioBt1?.id -> tvFragOutPut?.text = "You Opt in for additional content"
                radioBt2?.id -> tvFragOutPut?.text = "You DO NOT Opt in for additional content"
            }
        }

    }

    private fun fidApproach(view: View) {
        val rg: RadioGroup? = view.findViewById(R.id.rbGroup)
        val rb1: RadioButton? = view.findViewById(R.id.radioBt1)
        val rb2: RadioButton? = view.findViewById(R.id.radioBt2)
        val tvOut: TextView? = view.findViewById(R.id.tvFragOutPut)
        val cbDisable: CheckBox? = view.findViewById(R.id.cbox)

        rg?.setOnCheckedChangeListener { _, checkedId ->
            when (checkedId) {
                rb1?.id -> tvOut?.text = "You Opt in for additional content"
                rb2?.id -> tvOut?.text = "You DO NOT Opt in for additional content"
            }
        }

        rb1?.isChecked = true
        rb2?.isChecked = false

        cbDisable?.setOnCheckedChangeListener { _, bool ->
            rb1?.isEnabled = bool
            rb2?.isEnabled = bool
        }


    }


}

基本活动和片段:


abstract class BaseActivityCurrent :AppCompatActivity(){

    abstract val contentView: Int


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(contentView)
        setup()
    }

    abstract fun setup()

}
abstract class BaseFragmentCurrent : Fragment(){


    abstract val contentView: Int

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(contentView,container,false)
    }

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

        setup()
    }

    abstract fun setup()


}

如您所见,由于基础活动将完成所有繁重的工作,因此孩子班总是很容易扩展。并且由于合成材料被广泛使用,因此没有太大的问题。 若要将绑定类与前面提到的约束一起使用,我将:

  1. 需要子类实现将数据提供回父片段的功能。这是简单的部分,只需创建更多抽象函数即可返回孩子的绑定类的实例即可。

  2. 将子类的视图绑定存储在变量中(例如val binding:T),以便基类可以在销毁广告句柄时相应地将其无效。由于孩子的Binding类实例类型事先未知,因此有些棘手。但是将父级设为通用(<T:ViewBinding>)就可以了

  3. 将视图返回到系统以进行通货膨胀。再次,很容易,因为幸运的是,对于大多数组件,系统接受了一个放大的视图,并且拥有孩子的绑定实例将让我向系统提供一个视图

  4. 防止子类直接使用在点1中创建的路由。考虑一下:如果子类具有返回自己的绑定类实例的函数getBind(){...},为什么不使用它而使用super.binding呢?是什么阻止了他们在onDestroy()中使用getBind()函数,而不应该在其中访问绑定?

所以这就是为什么我使该函数为空并将可变列表传递给它的原因。子类现在将其绑定添加到父级将访问的列表中。如果他们不这样做,它将抛出NPE。如果他们尝试在摧毁或其他地方使用它,它将再次抛出illegalstate exception。我还创建了一个方便的高阶函数withBinding(..),以方便使用。

基本绑定活动和片段:



abstract class BaseActivityFinal<VB_CHILD : ViewBinding> : AppCompatActivity() {

    private var binding: VB_CHILD? = null


    //lifecycle
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(getInflatedLayout(layoutInflater))
        setup()
    }
    override fun onDestroy() {
        super.onDestroy()
        this.binding = null
    }


    //internal functions
    private fun getInflatedLayout(inflater: LayoutInflater): View {
        val tempList = mutableListOf<VB_CHILD>()
        attachBinding(tempList, inflater)
        this.binding = tempList[0]


        return binding?.root?: error("Please add your inflated binding class instance at 0th position in list")
    }

    //abstract functions
    abstract fun attachBinding(list: MutableList<VB_CHILD>, layoutInflater: LayoutInflater)

    abstract fun setup()

    //helpers
    fun withBinding(block: (VB_CHILD.() -> Unit)?): VB_CHILD {
        val bindingAfterRunning:VB_CHILD? = binding?.apply { block?.invoke(this) }
        return bindingAfterRunning
            ?:  error("Accessing binding outside of lifecycle: ${this::class.java.simpleName}")
    }


}

//--------------------------------------------------------------------------

abstract class BaseFragmentFinal<VB_CHILD : ViewBinding> : Fragment() {

    private var binding: VB_CHILD? = null


    //lifecycle
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ) = getInflatedView(inflater, container, false)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setup()
    }

    override fun onDestroy() {
        super.onDestroy()
        this.binding = null
    }


    //internal functions
    private fun getInflatedView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        attachToRoot: Boolean
    ): View {
        val tempList = mutableListOf<VB_CHILD>()
        attachBinding(tempList, inflater, container, attachToRoot)
        this.binding = tempList[0]
        return binding?.root
            ?: error("Please add your inflated binding class instance at 0th position in list")

    }

    //abstract functions
    abstract fun attachBinding(
        list: MutableList<VB_CHILD>,
        layoutInflater: LayoutInflater,
        container: ViewGroup?,
        attachToRoot: Boolean
    )

    abstract fun setup()

    //helpers
    fun withBinding(block: (VB_CHILD.() -> Unit)?): VB_CHILD {
        val bindingAfterRunning:VB_CHILD? = binding?.apply { block?.invoke(this) }
        return bindingAfterRunning
            ?:  error("Accessing binding outside of lifecycle: ${this::class.java.simpleName}")
    }

}

儿童活动和片段:


class MainActivityFinal:BaseActivityFinal<MainActivityBinding>() {
    var i = 0

    override fun setup() {
        supportFragmentManager.beginTransaction()
            .replace(R.id.container, MainFragmentFinal())
            .commitNow()

        viewBindingApproach()
    }
    
    private fun viewBindingApproach() {
        withBinding {
            btText.setOnClickListener { tvText.text = "The current click count is ${++i}"  }
            btText.performClick()
        }

    }
    
    override fun attachBinding(list: MutableList<MainActivityBinding>, layoutInflater: LayoutInflater) {
        list.add(MainActivityBinding.inflate(layoutInflater))
    }
}

//-------------------------------------------------------------------

class MainFragmentFinal : BaseFragmentFinal<MainFragmentBinding>() {
   
    override fun setup() {
        bindingApproach()
    }

    private fun bindingApproach() {
        withBinding {
            rbGroup.setOnCheckedChangeListener{ _, id ->
                when(id){
                    radioBt1.id -> tvFragOutPut.text = "You Opt in for additional content"
                    radioBt2.id -> tvFragOutPut.text = "You DO NOT Opt in for additional content"
                }
            }
            radioBt1.isChecked = true
            radioBt2.isChecked = false

            cbox.setOnCheckedChangeListener { _, bool ->
                radioBt1.isEnabled = !bool
                radioBt2.isEnabled = !bool
            }
        }
    }


    override fun attachBinding(
        list: MutableList<MainFragmentBinding>,
        layoutInflater: LayoutInflater,
        container: ViewGroup?,
        attachToRoot: Boolean
    ) {
        list.add(MainFragmentBinding.inflate(layoutInflater,container,attachToRoot))
    }


}


答案 5 :(得分:0)

我认为一个简单的反应是使用公共类的 bind 方法。

我知道这不适用于所有情况,但它适用于具有相似元素的视图。

如果我有两个布局 row_type_1.xmlrow_type_2.xml,它们共享公共元素,那么我可以执行以下操作:

ROW_TYPE_1 -> CommonRowViewHolder(
                    RowType1Binding.inflate(LayoutInflater.from(parent.context), parent, false))

然后对于类型 2,不是创建另一个接收其自己的 Binding 类的 ViewHolder,而是执行以下操作:

ROW_TYPE_2 -> {
    val type2Binding = RowType2Binding.inflate(LayoutInflater.from(parent.context), parent, false))
    CommonRowViewHolder(RowType1Binding.bind(type2Binding))
}

如果它是组件的子集,则可以放置继承

CommonRowViewHolder: ViewHolder {
    fun bind(binding: RowType1Holder)
}

Type2RowViewHolder: CommonRowViewHolder {

    fun bind(binding: RowType2Holder) {
        super.bind(Type1RowViewHolder.bind(binding))
        
        //perform specific views for type 2 binding ...
    }
}

答案 6 :(得分:0)

inline fun <reified BindingT : ViewBinding> AppCompatActivity.viewBindings(
    crossinline bind: (View) -> BindingT
) = object : Lazy<BindingT> {

    private var initialized: BindingT? = null

    override val value: BindingT
        get() = initialized ?: bind(
            findViewById<ViewGroup>(android.R.id.content).getChildAt(0)
        ).also {
            initialized = it
        }

    override fun isInitialized() = initialized != null
}

答案 7 :(得分:0)

我创建了这个抽象类作为基础;

abstract class BaseFragment<VB : ViewBinding> : Fragment() {

private var _binding: VB? = null

val binding get() = _binding!!

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

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

abstract fun inflateViewBinding(inflater: LayoutInflater, container: ViewGroup?): VB

}

用法;

class HomeFragment : BaseFragment<FragmentHomeBinding>() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    binding.textViewTitle.text = ""
}

override fun inflateViewBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentHomeBinding {
    return FragmentHomeBinding.inflate(inflater, container, false)
}

}

答案 8 :(得分:-1)

这是伟大的 Chetan Gupta's answer 的稍微修改的 Kotlin 版本。 避免使用“UNCHECKED_CAST”。

活动

abstract class BaseViewBindingActivity<ViewBindingType : ViewBinding> : AppCompatActivity() {

    protected lateinit var binding: ViewBindingType
    protected abstract val bindingInflater: (LayoutInflater) -> ViewBindingType

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = bindingInflater.invoke(layoutInflater)
        val view = binding.root
        setContentView(view)
    }
}

片段

abstract class BaseViewBindingFragment<ViewBindingType : ViewBinding> : Fragment() {

    private var _binding: ViewBindingType? = null
    protected val binding get() = requireNotNull(_binding)
    protected abstract val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> ViewBindingType

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

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