Android-Kotlin基础(Jetpack②-Data Binding)

功能需求

显示用户的姓名、年龄、身份(学生 / 上班族)

点击按钮切换用户信息

一、传统方式实现

1. 布局文件(activity_main.xml)
XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/tvName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"/>

    <TextView
        android:id="@+id/tvAge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"/>

    <TextView
        android:id="@+id/tvIdentity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"/>

    <Button
        android:id="@+id/btnChange"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="切换用户"
        android:layout_marginTop="16dp"/>

</LinearLayout>
2. Activity 代码(MainActivity.kt)
Kotlin 复制代码
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView

class MainActivity : AppCompatActivity() {
    // 声明所有需要操作的控件
    private lateinit var tvName: TextView
    private lateinit var tvAge: TextView
    private lateinit var tvIdentity: TextView
    private lateinit var btnChange: Button

    // 数据
    private var currentUser = User("张三", 20, true)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 1. 手动绑定所有控件(最繁琐的步骤)
        initViews()

        // 2. 手动更新UI
        updateUI()

        // 3. 手动设置点击事件
        btnChange.setOnClickListener {
            // 切换数据
            currentUser = if (currentUser.name == "张三") {
                User("李四", 25, false)
            } else {
                User("张三", 20, true)
            }
            // 手动更新UI
            updateUI()
        }
    }

    // 初始化所有控件(findViewById重复劳动)
    private fun initViews() {
        tvName = findViewById(R.id.tvName)
        tvAge = findViewById(R.id.tvAge)
        tvIdentity = findViewById(R.id.tvIdentity)
        btnChange = findViewById(R.id.btnChange)
    }

    // 手动更新所有控件内容
    private fun updateUI() {
        tvName.text = currentUser.name
        tvAge.text = "年龄:${currentUser.age}"
        tvIdentity.text = if (currentUser.isStudent) "学生" else "上班族"
    }

    // 数据类
    data class User(
        val name: String,
        val age: Int,
        val isStudent: Boolean
    )
}

二、Data Binding 方式实现

1. 布局文件(activity_main.xml)
XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="user"
            type="com.example.databindingdemo.MainActivity.User" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="16dp">

        <!-- 直接绑定数据,无需id -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}"  <!-- 数据绑定 -->
            android:textSize="18sp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{`年龄:` + user.age}"  <!-- 表达式拼接 -->
            android:layout_marginTop="8dp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.isStudent ? `学生` : `上班族`}"  <!-- 条件判断 -->
            android:layout_marginTop="8dp"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="切换用户"
            android:layout_marginTop="16dp"
            android:onClick="@{() -> user.change()}"  <!-- 绑定点击事件 -->
            android:enabled="@{user.name.isNotEmpty()}"/>  <!-- 动态控制启用状态 -->

    </LinearLayout>
</layout>
2. Activity 代码(MainActivity.kt)
Kotlin 复制代码
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.databindingdemo.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    // 只需要声明Binding对象
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 初始化Binding(替代setContentView和findViewById)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 绑定数据(自动更新UI)
        binding.user = User("张三", 20, true)
    }

    // 数据类(包含修改逻辑)
    inner class User(
        val name: String,
        val age: Int,
        val isStudent: Boolean
    ) {
        // 点击事件处理
        fun change() {
            // 直接修改绑定的数据,UI自动更新
            binding.user = if (name == "张三") {
                User("李四", 25, false)
            } else {
                User("张三", 20, true)
            }
        }
    }
}

三、核心差异对比

对比维度 传统方式 Data Binding 方式
控件获取 需手动写大量 findViewById 自动生成 Binding 类,直接通过属性访问
UI 更新 需手动调用 setText() 等方法 数据变化后自动更新 UI
代码量 多(初始化、更新逻辑重复) 少(省略模板代码)
可读性 数据与 UI 关联分散在代码中 数据与 UI 关联集中在布局中
维护成本 高(修改 UI 需同时改代码) 低(布局与数据绑定一目了然)
错误检查 运行时可能因控件 ID 错误崩溃 编译时检查绑定表达式,提前发现错误
事件处理 需手动设置 setOnClickListener 布局中直接绑定事件,逻辑更集中

Data Binding 方式的区别

当使用 Data Binding 时,布局文件会被自动处理成一个 Binding 类 (比如 activity_main.xml 会生成 ActivityMainBinding 类),这个类相当于布局文件的 "代码代表"。

第一行:binding = ActivityMainBinding.inflate(layoutInflater)
  • ActivityMainBinding:自动生成的类,名字是布局文件名(下划线转驼峰)+ Binding
  • inflate(layoutInflater):这是解析布局的过程,相当于传统的 "把 XML 变成 View"
  • 执行后,binding 对象就持有了整个布局的所有控件和数据绑定能力
第二行:setContentView(binding.root)
  • binding.root:指的是布局文件的根节点(比如你布局中的 LinearLayoutConstraintLayout
  • 这行代码的作用和传统 setContentView(R.layout.xxx) 一样:让 Activity 显示这个根节点对应的界面
  • 区别是:传统方式直接传布局 ID,Data Binding 方式传已经解析好的根 View

形象比喻

可以把布局文件想象成 "设计图纸":

  • 传统方式:直接告诉 Activity "用图纸 ID 302 建房子"(setContentView(R.layout.xxx)),之后需要自己去房子里找各个房间(findViewById)。
  • Data Binding 方式:先请一个 "施工队"(ActivityMainBinding.inflate())根据图纸建好房子,得到一个 "房屋管理手册"(binding),手册里记录了所有房间的位置。然后告诉 Activity "就用这个建好的房子"(setContentView(binding.root)),之后想找哪个房间,直接查手册就行(binding.tvName)。