Kotlin自定义控件中View Binding的使用解析

使用 GPT-4o 回答,作者 GPT4oMini.app

问题:

用Kotlin开发Android应用,自定义控件TitleLayout.kt如下: package com.example.activitytest

import android.app.Activity import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater //import android.widget.Button import android.widget.Toast import androidx.constraintlayout.widget.ConstraintLayout import com.example.activitytest.databinding.TitleLayoutBinding

class TitleLayout(context: Context, attrs: AttributeSet) : ConstraintLayout(context, attrs) {

private val binding: TitleLayoutBinding = TitleLayoutBinding.inflate(LayoutInflater.from(context), this, true)
init {
    // 先膨胀布局
    //LayoutInflater.from(context).inflate(R.layout.title_layout, this, true)
    //val titleBack: Button = findViewById(R.id.titleBack)
    //val titleEdit: Button = findViewById(R.id.titleEdit)

    binding.titleBack.setOnClickListener {
        val activity = context as Activity
        activity.finish()
    }
    binding.titleEdit.setOnClickListener {
        Toast.makeText(context, "You clicked Edit button", Toast.LENGTH_SHORT).show()
    }
}

}

title_layout.xml如下:

<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout 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">

<Button
    android:id="@+id/titleBack"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Back"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent" />

<TextView
    android:id="@+id/titleText"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:text="Title Text"
    android:textColor="#fff"
    android:textSize="24sp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toStartOf="@+id/titleEdit"
    app:layout_constraintStart_toEndOf="@+id/titleBack"
    app:layout_constraintTop_toTopOf="parent" />

<Button
    android:id="@+id/titleEdit"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Edit"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

我有个疑问,在TitleLayout.kt中采用了View Binding,且在代码里把“LayoutInflater.from(context).inflate(R.layout.title_layout, this, true)”注释掉了,那么程序是在哪里确定TitleLayout.kt采用了R.layout.title_layout这个布局的?

答案:

TitleLayout.kt 中,虽然你注释掉了 LayoutInflater.from(context).inflate(R.layout.title_layout, this, true) 这一行代码,但你仍然通过 View Binding 的方式将布局文件 title_layout.xml 绑定到了 TitleLayout 这个自定义控件上。

具体来说,以下这行代码实现了布局的膨胀和绑定:

private val binding: TitleLayoutBinding = TitleLayoutBinding.inflate(LayoutInflater.from(context), this, true)

这行代码做了以下几件事情:

  1. 获取 LayoutInflater:通过 LayoutInflater.from(context) 获取 LayoutInflater 实例。
  2. 膨胀布局TitleLayoutBinding.inflate 方法会使用 LayoutInflatertitle_layout.xml 布局文件膨胀为一个 View 对象。
  3. 绑定到当前 ViewGroupthis 代表当前的 TitleLayout 实例,true 表示将膨胀的布局添加到当前的 TitleLayout 中。

因此,虽然你注释掉了手动膨胀布局的代码,但通过 View Binding 的方式,布局文件 title_layout.xml 仍然被正确地膨胀并绑定到了 TitleLayout 上。

View Binding 的工作原理

View Binding 是 Android 提供的一种更安全、更高效的方式来访问布局中的视图。它会在编译时为每个布局文件生成一个绑定类。对于 title_layout.xml 布局文件,View Binding 会生成一个名为 TitleLayoutBinding 的类。

TitleLayoutBinding 类中,有一个静态的 inflate 方法,这个方法会:

  1. 使用 LayoutInflater 膨胀布局文件。
  2. 创建并返回一个 TitleLayoutBinding 实例,这个实例包含了对布局中所有视图的引用。

通过这种方式,你可以避免手动调用 findViewById,并且在编译时就能发现视图引用的错误。

总结

TitleLayout.kt 中,虽然你注释掉了手动膨胀布局的代码,但通过 View Binding 的方式,布局文件 title_layout.xml 仍然被正确地膨胀并绑定到了 TitleLayout 上。View Binding 的 inflate 方法不仅膨胀了布局,还将其添加到当前的 TitleLayout 中,并返回一个包含所有视图引用的绑定类实例。