Skip to content

DataBinding - 数据绑定

源:数据绑定库

使用声明性格式将布局中的界面组件绑定到应用中的数据源。允许开发者将 UI 组件直接绑定到应用的数据源。这种方式可以减少样板代码,提高代码的可读性和可维护性。通过 Data Binding,开发者可以实现双向数据绑定,使得数据和 UI 的变化能够自动同步。

优点

  • 减少样板代码:通过 XML 布局文件直接绑定数据,减少了在 Activity 或 Fragment 中的代码量。
  • 提高可维护性:UI 组件和数据逻辑分离,使得代码结构更加清晰。
  • 支持双向数据绑定:数据变化时自动更新 UI,UI 变化时也能更新数据模型。

缺点

DataBing虽然带来了很多方便,但是在实际使用中,有时体验却不是那么好

  • "代码显得很杂乱"。虽然它减少了很多样板代码,但是它需要修改每个.xml布局(xml布局根节点都要改成<layout></layout>)结构, 在xml里面加了很多绑定代码,导致整个界面看起来很乱。

    由于需要绑定适配器,Java/Kotlin代码中也需要额外添加很多注解,所以随着功能注解的不断添加,代码也有可能会更难维护

  • "一些奇怪的编译问题"。DataBinding会使用注解生成很多额外的代码,在编译运行时可能会由于缓存等因素,导致代码或中经常一堆爆红,需要经常clean项目才行

所以,一般情况下还是建议尽量使用 ViewBinding

⭐ 注意

在许多情况下,视图绑定可提供与数据绑定相同的优势,因为它的实现更简单、性能更高。 如果您主要使用数据绑定来取代 findViewById() 调用,请考虑改用视图绑定

开始使用

数据绑定库与 Android Gradle 插件捆绑在一起。您无需声明对此库的依赖项,但必须启用它。

如需启用数据绑定,请在模块的 build.gradle 文件中将 dataBinding 构建选项设置为 true,如下所示:

kotlin
android {
  buildFeatures {
    dataBinding = true
  }
}

基本数据绑定

传统方式

在传统的 Android 开发中,您通常需要在 ActivityFragment 中使用 findViewById() 方法来查找视图并设置其属性。 例如:

xml

<TextView
        android:id="@+id/sample_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
kotlin
class MainActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()

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

        val textView: TextView = findViewById(R.id.sample_text)
        textView.text = viewModel.userName
    }
}

数据绑定方式

使用数据绑定库后,您可以直接在布局文件中使用绑定表达式来设置视图的属性。

先修改布局根目录结构,加上<layout></layout>(可以将鼠标放到布局文件的第一行选择Convert to data binding layout 自动转换。即可将布局文件改造成DataBinding 格式。DataBindingxml根布局都要是这这个,后续不再额外描述)

然后修改 TextView,添加 android:text="@{viewModel.userName}" , 这样便会将 viewModel.userName 的数据绑定到了 sample_text Text:

xml

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
                name="viewmodel"
                type="com.myapp.data.ViewModel"/>
    </data>
    <TextView
            android:id="@+id/sample_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewModel.userName}"/>
</layout>
kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private val viewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.viewModel = viewModel
        binding.lifecycleOwner = this // 使 LiveData 能够自动更新 UI
    }
}

绑定表达式

传统方式

在传统方式中,您需要在 Activity 中手动设置每个视图的属性:

kotlin
// 传统方式
val textView: TextView = findViewById(R.id.sample_text)
textView.text = viewModel.userName

val button: Button = findViewById(R.id.button)
button.setOnClickListener {
    viewModel.onButtonClick()
}

数据绑定方式

使用数据绑定库,您可以在布局文件中直接绑定视图和数据:

xml

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
                name="viewModel"
                type="com.myapp.data.UserViewModel"/>
    </data>
    <ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <TextView
                android:id="@+id/sample_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{viewModel.userName}"/>

        <Button
                android:id="@+id/button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Click Me"
                android:onClick="@{() -> viewModel.onButtonClick()}"/>
    </ConstraintLayout>
</layout>

绑定可观察的数据对象

在 ViewModel 中,您可以使用 LiveData:

kotlin
class UserViewModel : ViewModel() {
    val userName = MutableLiveData<String>()

    init {
        userName.value = "Alice"
    }

    fun onButtonClick() {
        userName.value = "Bob"
    }
}

传统方式

在传统方式中,您需要手动监听数据的变化并更新 UI,当数据源发生变化时:

kotlin
// 传统方式
viewModel.userName.observe(this, Observer { newName ->
    textView.text = newName
})

数据绑定方式

使用数据绑定库,您可以直接在布局中绑定可观察的数据对象,UI 会自动更新:

xml

<TextView
        android:id="@+id/sample_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{viewModel.userName}"/>

绑定适配器

传统方式

在传统方式中,您需要手动设置视图的属性:

kotlin
// 传统方式
button.setOnClickListener {
    if (viewModel.isButtonVisible) {
        button.visibility = View.VISIBLE
    } else {
        button.visibility = View.GONE
    }
}

数据绑定方式

绑定适配器允许您为自定义属性创建自定义逻辑。

对于每个布局表达式,都有一个绑定适配器,可进行框架调用以设置相应的属性或监听器。

例如,绑定适配器可以负责调用 setText() 方法来设置文本属性,或调用 setOnClickListener() 方法将监听器添加到点击事件。 您可以在 android.databinding.adapters 软件包中使用最常见的绑定适配器,您还可以创建自定义适配器,如以下示例所示:

kotlin
@BindingAdapter("app:goneUnless")
fun goneUnless(view: View, visible: Boolean) {
    view.visibility = if (visible) View.VISIBLE else View.GONE
}

在布局文件中使用自定义适配器:

xml

<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:goneUnless="@{viewModel.isButtonVisible}"/>

双向数据绑定

传统方式

在传统方式中,数据更改需要手动更新UI,UI变化(比如输入框输入数据)需要手动监听并更改数据:

kotlin

editText.text = viewModel.userName

editText.addTextChangedListener(object : TextWatcher {
    override fun afterTextChanged(s: Editable?) {
        viewModel.userName = s.toString()
    }
})

数据绑定方式

数据绑定库支持双向数据绑定,允许 UI 和数据模型之间的双向同步。例如,您可以将 EditText 的文本与 ViewModel 中的属性绑定:

xml

<EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@={viewModel.userName}"/>

其他资源

如需详细了解数据绑定,请参阅以下资源(可能需要代理):

Android 官方文档 - 数据绑定

Android Data Binding 库 - 只需两步即可从可观察字段到 LiveData

Data Binding - 经验教训