📚 Data Binding: <include> 传递 ViewModel 笔记

核心原理

在 Android Data Binding 中,ViewModel 的传递是通过 <include> 标签上的 bind: 属性实现的一种显式赋值机制。

  • 父布局中声明的变量不会自动传递给子布局。
  • 要传递,必须在 <include> 标签中指明:bind:【子布局变量名】="@{【父布局变量实例】}"
  • ViewModel 的生命周期和响应式更新(ObservableField / LiveData)由父布局设置的 lifecycleOwner 统一管理。
  • MVVM中的<include>布局复用:viewModel传递 - 简书

1. ViewModel (数据源)

假设我们需要一个包含布尔状态的 ViewModel

// MyComponentViewModel.kt
import androidx.lifecycle.ViewModel
import androidx.databinding.ObservableField

class MyComponentViewModel : ViewModel() {
    // 示例:需要被子布局观察和操作的状态
    val isToggled = ObservableField(false)

    fun toggle() {
        isToggled.set(!isToggled.get())
    }
}

2. 子布局 (layout_reusable_component.xml)

子布局必须使用 <data> 块声明一个 <variable> 作为接口来接收 ViewModel

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
                name="componentVm"
                type="com.example.viewmodel.MyComponentViewModel" />
    </data>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

        <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{componentVm.isToggled ? `状态: ON` : `状态: OFF`}"
                android:onClick="@{() -> componentVm.toggle()}" />
          
    </LinearLayout>
</layout>

3. 父布局 (activity_main.xml)

父布局必须持有 ViewModel 实例,并在 <include> 标签上进行显式绑定

<?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">
  
    <data>
        <variable
                name="mainVm"
                type="com.example.viewmodel.MyComponentViewModel" />
    </data>

    <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
  
        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{`全局状态: ` + mainVm.isToggled}" />

        <include
                android:id="@+id/included_component"
                layout="@layout/layout_reusable_component"
                bind:componentVm="@{mainVm}" />
  
    </LinearLayout>
</layout>

4. Activity/Fragment (Kotlin 代码)

Activity/Fragment 只需要完成主布局的绑定。


// MainActivity.kt

class MainActivity : AppCompatActivity() {
  
    // 实例化 ViewModel
    private val myViewModel: MyComponentViewModel by viewModels() 

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  
        binding.apply {
            // 步骤 4: 为主 Binding 设置 ViewModel 实例
            this.mainVm = myViewModel 
      
            // 步骤 5: 设置 lifecycleOwner,确保数据响应式更新向下传递
            lifecycleOwner = this@MainActivity
        }
    }
}

5. 关键点总结

检查项 说明 强制要求
子布局变量 必须在 <data> 中声明 <variable> 来接收参数。 必须存在
bind: 属性 <include> 上使用 bind:子变量名="@{父实例}" 进行传递。 必须使用 bind: 前缀
变量名匹配 bind:componentVm 必须与子布局中的 name="componentVm" 一致。 名称必须匹配
lifecycleOwner 必须在 Activity/Fragment 中设置 binding.lifecycleOwner = this