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)
}
}
以上是对比以及快速入门参考案例。