Adroid Data Binding数据绑定对比(findViewXX、ButterKnife)

9.1 数据绑定基础对比

kotlin 复制代码
// 传统方式 - findViewById
class UserActivity : AppCompatActivity() {
    private lateinit var tvName: TextView
    private lateinit var tvAge: TextView
    private lateinit var tvEmail: TextView
    private lateinit var tvPhone: TextView
    private lateinit var tvAddress: TextView
    private lateinit var btnUpdate: Button
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)
        
        // 大量的 findViewById 调用
        tvName = findViewById(R.id.tvName)
        tvAge = findViewById(R.id.tvAge)
        tvEmail = findViewById(R.id.tvEmail)
        tvPhone = findViewById(R.id.tvPhone)
        tvAddress = findViewById(R.id.tvAddress)
        btnUpdate = findViewById(R.id.btnUpdate)
        
        // 加载用户数据
        loadUserData()
    }
    
    private fun loadUserData() {
        val user = userRepository.getUser("123")
        // 手动更新每个 View
        tvName.text = user.name
        tvAge.text = user.age.toString()
        tvEmail.text = user.email
        tvPhone.text = user.phone
        tvAddress.text = user.address
    }
    
    private fun updateUser(user: User) {
        // 手动更新每个 View
        tvName.text = user.name
        tvAge.text = user.age.toString()
        tvEmail.text = user.email
        tvPhone.text = user.phone
        tvAddress.text = user.address
    }
}



    // ===================== 1. ButterKnife 绑定控件 =====================
    @BindView(R.id.tv_name)  // 对应布局 TextView id
    TextView tvName;

    @BindView(R.id.tv_age)
    TextView tvAge;
java 复制代码
//  - ButterKnife
    @BindView(R.id.tv_name)  // 对应布局 TextView id
    TextView tvName;

    @BindView(R.id.tv_age)
    TextView tvAge;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 1. 初始化 ButterKnife
        ButterKnife.bind(this);
    
        // 3. 直接赋值!
        setDataToView();
    }

传统方式的问题

问题 说明 影响
样板代码 大量的 findViewById 调用 代码冗余,难以维护
手动更新 需要手动更新每个 View 容易遗漏,容易出错
可读性差 UI 逻辑与业务逻辑混合 代码可读性差
类型不安全 使用字符串 ID 获取 View 运行时错误
难以测试 UI 逻辑与业务逻辑耦合 难以单元测试
kotlin 复制代码
// 使用 Data Binding
class UserActivity : AppCompatActivity() {
    
    private lateinit var binding: ActivityUserBinding
    private lateinit var viewModel: UserViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 设置绑定
        binding = DataBindingUtil.setContentView(this, R.layout.activity_user)
        viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        
        // 设置数据
        binding.viewModel = viewModel
        binding.lifecycleOwner = this
        
        // 数据更新会自动反映到 UI 在xml布局文件中使用User
        viewModel.loadUser("123")
    }
}

Data Binding 的优势

优势 说明
消除样板代码 自动生成 View 的引用,无需 findViewById
自动 UI 更新 数据变化自动反映到 UI
类型安全 编译时检查,避免运行时错误
代码简洁 UI 逻辑与业务逻辑分离
易于测试 UI 逻辑可以独立测试

9.2 Data Binding 快速入门

9.2.1 启用 Data Binding

在 build.gradle 中启用

gradle 复制代码
android {
    // ...
    dataBinding {
        enabled = true
    }
}

9.2.2 创建布局文件

布局文件结构

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <!-- 定义数据变量 -->
        <variable
            name="user"
            type="com.example.app.model.User" />
        <variable
            name="viewModel"
            type="com.example.app.viewmodel.UserViewModel" />
        
        <import type="android.view.View" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".ui.user.UserActivity">
        <!-- 用户名 -->
        <TextView
            android:id="@+id/tvName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}"
            android:textSize="20sp"
            android:textStyle="bold"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginTop="50dp" />
        <!-- 年龄 -->
        <TextView
            android:id="@+id/tvAge"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{@string/age_format(user.age)}"
            android:textSize="16sp"
            app:layout_constraintTop_toBottomOf="@id/tvName"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginTop="20dp" />
        <!-- 邮箱 -->
        <TextView
            android:id="@+id/tvEmail"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.email}"
            android:textSize="16sp"
            app:layout_constraintTop_toBottomOf="@id/tvAge"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginTop="20dp" />
        <!-- 电话 -->
        <TextView
            android:id="@+id/tvPhone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.phone}"
            android:textSize="16sp"
            app:layout_constraintTop_toBottomOf="@id/tvEmail"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginTop="20dp" />

        <!-- 地址 -->
        <TextView
            android:id="@+id/tvAddress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.address}"
            android:textSize="16sp"
            app:layout_constraintTop_toBottomOf="@id/tvPhone"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginTop="20dp" />

        <!-- 更新按钮 -->
        <Button
            android:id="@+id/btnUpdate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="更新"
            android:onClick="@{() -> viewModel.updateUser()}"
            android:visibility="@{viewModel.isLoading ? View.GONE : View.VISIBLE}"
            app:layout_constraintTop_toBottomOf="@id/tvAddress"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginTop="30dp" />

        <!-- 加载进度条 -->
        <ProgressBar
            android:id="@+id/progressBar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="@{viewModel.isLoading ? View.VISIBLE : View.GONE}"
            app:layout_constraintTop_toBottomOf="@id/tvAddress"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginTop="30dp" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

9.2.3 在 Activity 中使用 Data Binding

kotlin 复制代码
/**
 * 用户 Activity
 */
class UserActivity : AppCompatActivity() {
    
    private lateinit var binding: ActivityUserBinding
    private lateinit var viewModel: UserViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 使用 DataBindingUtil.setContentView 代替 setContentView
        binding = DataBindingUtil.setContentView(this, R.layout.activity_user)
        viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        
        // 设置数据到绑定对象
        binding.viewModel = viewModel
        binding.lifecycleOwner = this
        
        // 加载用户数据
        val userId = intent.getStringExtra("USER_ID") ?: ""
        viewModel.loadUser(userId)
    }
}

以上是对比以及快速入门参考案例。

相关推荐
程序员陆业聪4 小时前
两次Flutter全屏白踩坑复盘:Layout的静默失败,以及AI结对编程的认知盲区
android
程序员陆业聪5 小时前
Compose Strong Skipping Mode 的真相:它并不会让你的类型变 Stable
android
阿巴斯甜9 小时前
launch 和 async 内部都是串行,为什么还能实现并发?
kotlin
shaoming377610 小时前
浏览器动作开发:地址栏图标点击事件、弹出页面设计
android·mysql·adb
古怪今人10 小时前
Gradle构建工具 Groovy/Kotlin DSL的现代化自动化构建工具
开发语言·kotlin·自动化
赏金术士10 小时前
Kotlin 协程与挂起函数(Coroutines & suspend)入门到实战
android·开发语言·kotlin
泡泡以安12 小时前
Unidbg学习笔记(十三):固定随机干扰项
android·逆向
泡泡以安12 小时前
Unidbg学习笔记(十六):Console Debugger
android·逆向
赏金术士12 小时前
Room + Flow 完整教程(现代 Android 官方方案)
android·kotlin·room·compose
泡泡以安12 小时前
Unidbg学习笔记(八):文件系统层补环境
android·逆向