9.1 数据绑定基础对比
// 传统方式 - findViewByIdclassUserActivity:AppCompatActivity(){privatelateinitvartvName:TextViewprivatelateinitvartvAge:TextViewprivatelateinitvartvEmail:TextViewprivatelateinitvartvPhone:TextViewprivatelateinitvartvAddress:TextViewprivatelateinitvarbtnUpdate:ButtonoverridefunonCreate(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()}privatefunloadUserData(){valuser=userRepository.getUser("123")// 手动更新每个 ViewtvName.text=user.name tvAge.text=user.age.toString()tvEmail.text=user.email tvPhone.text=user.phone tvAddress.text=user.address}privatefunupdateUser(user:User){// 手动更新每个 ViewtvName.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 idTextView tvName;@BindView(R.id.tv_age)TextView tvAge;// - ButterKnife@BindView(R.id.tv_name)// 对应布局 TextView idTextViewtvName;@BindView(R.id.tv_age)TextViewtvAge;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 1. 初始化 ButterKnifeButterKnife.bind(this);// 3. 直接赋值!setDataToView();}传统方式的问题:
| 问题 | 说明 | 影响 |
|---|---|---|
| 样板代码 | 大量的 findViewById 调用 | 代码冗余,难以维护 |
| 手动更新 | 需要手动更新每个 View | 容易遗漏,容易出错 |
| 可读性差 | UI 逻辑与业务逻辑混合 | 代码可读性差 |
| 类型不安全 | 使用字符串 ID 获取 View | 运行时错误 |
| 难以测试 | UI 逻辑与业务逻辑耦合 | 难以单元测试 |
// 使用 Data BindingclassUserActivity:AppCompatActivity(){privatelateinitvarbinding:ActivityUserBindingprivatelateinitvarviewModel:UserViewModeloverridefunonCreate(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布局文件中使用UserviewModel.loadUser("123")}}Data Binding 的优势:
| 优势 | 说明 |
|---|---|
| 消除样板代码 | 自动生成 View 的引用,无需 findViewById |
| 自动 UI 更新 | 数据变化自动反映到 UI |
| 类型安全 | 编译时检查,避免运行时错误 |
| 代码简洁 | UI 逻辑与业务逻辑分离 |
| 易于测试 | UI 逻辑可以独立测试 |
9.2 Data Binding 快速入门
9.2.1 启用 Data Binding
在 build.gradle 中启用:
android { // ... dataBinding { enabled = true } }9.2.2 创建布局文件
布局文件结构:
<?xml version="1.0" encoding="utf-8"?><layoutxmlns: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><!-- 定义数据变量 --><variablename="user"type="com.example.app.model.User"/><variablename="viewModel"type="com.example.app.viewmodel.UserViewModel"/><importtype="android.view.View"/></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".ui.user.UserActivity"><!-- 用户名 --><TextViewandroid: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"/><!-- 年龄 --><TextViewandroid: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"/><!-- 邮箱 --><TextViewandroid: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"/><!-- 电话 --><TextViewandroid: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"/><!-- 地址 --><TextViewandroid: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"/><!-- 更新按钮 --><Buttonandroid: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"/><!-- 加载进度条 --><ProgressBarandroid: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
/** * 用户 Activity */classUserActivity:AppCompatActivity(){privatelateinitvarbinding:ActivityUserBindingprivatelateinitvarviewModel:UserViewModeloverridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)// 使用 DataBindingUtil.setContentView 代替 setContentViewbinding=DataBindingUtil.setContentView(this,R.layout.activity_user)viewModel=ViewModelProvider(this).get(UserViewModel::class.java)// 设置数据到绑定对象binding.viewModel=viewModel binding.lifecycleOwner=this// 加载用户数据valuserId=intent.getStringExtra("USER_ID")?:""viewModel.loadUser(userId)}}以上是对比以及快速入门参考案例。