首页 / Android 入门教程 / 数据绑定与 ViewBinding

Android 入门教程

数据绑定与 ViewBinding

本教程共 100 篇 · 第 96 篇 · 更新于 2026-07-28 · 约 7 分钟阅读

AndroidAndroid 入门教程ViewBindingDataBindingXML

96. 数据绑定与 ViewBinding

本节目标:理解 ViewBinding 和 DataBinding 的区别和用法,学会用 ViewBinding 替代 findViewById、用 DataBinding 在 XML 写表达式、双向绑定,知道 Compose 时代还需不需要。

View 体系里,找 View 用 findViewById,类型不安全、容易 NPE。ViewBinding 和 DataBinding 是两个替代方案。Compose 项目基本不用,但老代码维护要会。

两者区别

特性ViewBindingDataBinding
功能类型安全找 View找 View + XML 写表达式 + 双向绑定
编译速度慢(要生成额外代码)
包体积
学习成本
Compose 时代还能用完全没必要

简单场景用 ViewBinding,复杂绑定用 DataBinding。Compose 项目直接不用。

ViewBinding

开启

build.gradle.kts

android {
    buildFeatures {
        viewBinding = true
    }
}

用法

每个 XML 布局会生成一个绑定类,类名是 XML 名转 PascalCase + Binding。比如 activity_main.xml -> ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 直接用 binding.viewId
        binding.titleText.text = "Hello"
        binding.submitButton.setOnClickListener {
            // ...
        }
    }
}

XML:

<LinearLayout ...>
    <TextView android:id="@+id/title_text" ... />
    <Button android:id="@+id/submit_button" ... />
</LinearLayout>

title_text 变成 binding.titleText,类型是 TextView。不用强转,不用判空(除非 visibility 是 gone 且没 id)。

Fragment 里用

class MyFragment : Fragment() {
    private var _binding: MyFragmentBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = MyFragmentBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null  // 防止泄漏
    }
}
Warning

Fragment 的视图生命周期比 Fragment 短,onDestroyView 要把 binding 置 null,否则持有已销毁的 View 泄漏。

RecyclerView Adapter

class MyAdapter : RecyclerView.Adapter<MyAdapter.VH>() {
    class VH(val binding: ItemBinding) : RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
        val binding = ItemBinding.inflate(
            LayoutInflater.from(parent.context), parent, false
        )
        return VH(binding)
    }

    override fun onBindViewHolder(holder: VH, position: Int) {
        holder.binding.title.text = items[position]
    }
}

include 布局

<include
    android:id="@+id/included"
    layout="@layout/included_layout" />
binding.included.someTextView.text = "..."

include 必须有 id 才能 access。

DataBinding

开启

android {
    buildFeatures {
        dataBinding = true
    }
}

XML 改造

DataBinding 的 XML 要包一层 <layout>

<layout xmlns:android="...">
    <data>
        <variable
            name="user"
            type="com.example.User" />
    </data>

    <LinearLayout ...>
        <TextView
            android:text="@{user.name}"
            ... />
        <TextView
            android:text="@{String.valueOf(user.age)}"
            ... />
    </LinearLayout>
</layout>

绑定数据

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityMainBinding = DataBindingUtil.setContentView(
            this, R.layout.activity_main
        )
        binding.user = User("张三", 28)
    }
}

XML 里的 @{user.name} 自动绑定。

表达式

XML 里能写简单表达式:

<TextView
    android:text="@{user.firstName + ' ' + user.lastName}"
    android:visibility="@{user.age >= 18 ? View.VISIBLE : View.GONE}" />

支持的运算:

  • 算术:+ - * / %
  • 字符串拼接:+
  • 逻辑:&& || !
  • 比较:== != > < >= <=
  • 三元:?:
  • null 安全:???.

Observable 数据

数据变化时自动更新 UI:

class User : BaseObservable() {
    @get:Bindable
    var name: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.name)
        }
}

或者用 ObservableField

class User {
    val name = ObservableField<String>("")
}

binding.user = User().apply { name.set("张三") }

双向绑定

@={} 表示双向:UI 改了同步回数据。

<EditText
    android:text="@={user.name}" />

用户输入会自动写到 user.name

BindingAdapter

自定义 XML 属性:

@BindingAdapter("imageUrl")
fun ImageView.loadImage(url: String?) {
    if (url != null) {
        Glide.with(this).load(url).into(this)
    }
}

XML 用:

<ImageView
    app:imageUrl="@{user.avatarUrl}" />

ViewBinding vs DataBinding

场景选什么
只想替代 findViewByIdViewBinding
想在 XML 写表达式DataBinding
双向绑定DataBinding
自定义属性绑定DataBinding
性能要求高ViewBinding
Compose 项目都不用
Tip

ViewBinding 简单可靠,90% 场景够用。DataBinding 功能强但编译慢,复杂场景才用。

Compose 时代

Compose 完全不需要 ViewBinding 和 DataBinding:

  • 找 View 不存在了,Composable 直接调。
  • 状态绑定就是 mutableStateOf + Composable 参数。
  • 双向绑定就是状态提升。
@Composable
fun NameInput() {
    var name by remember { mutableStateOf("") }
    TextField(
        value = name,
        onValueChange = { name = it }
    )
}

这是「双向绑定」的等价物,但更简单清晰。

迁移建议

老项目从 findViewById 迁移:

  1. 第一步:开 ViewBinding,所有 findViewById 换成 binding.xxx
  2. 第二步:复杂页面用 DataBinding,把 UI 逻辑移到 XML。
  3. 第三步:新功能用 Compose,逐步替换。

不建议 DataBinding 老代码强行迁到 ViewBinding,差异大、收益小。直接迁 Compose 更划算。

常见坑

  1. ViewBinding 不生成:检查 buildFeatures.viewBinding = true,Sync 一下。
  2. Fragment 里 binding 没置空:泄漏,onDestroyView 必须 null。
  3. include 没 id:访问不到。
  4. DataBinding 表达式语法错:编译报错,但错误信息不直观,仔细看。
  5. DataBinding 用错 @{}@={}:单向双向混。
  6. ObservableField vs Flow:DataBinding 老 API,新代码用 StateFlow + observeAsState。
  7. BR 类不生成:DataBinding 编译产物, rebuild 一下。

小结

ViewBinding 类型安全替代 findViewById,简单可靠。DataBinding 在 XML 写表达式、支持双向绑定和 BindingAdapter,复杂场景用。开启方式是 buildFeatures 里加 viewBinding / dataBinding。Compose 项目完全不需要这两套,状态提升就是天然的绑定。老项目维护要会,新项目用 Compose。

下一章讲依赖注入。