使用可观察数据对象

可观察性是指对象能够通知其他对象其数据更改的能力。数据绑定库允许您使对象、字段或集合可观察。

您可以使用任何对象进行数据绑定,但修改对象不会自动导致 UI 更新。您可以使用数据绑定赋予您的数据对象通知其他对象(称为侦听器)其数据更改的能力。有三种类型的可观察类:字段集合对象

当这些可观察数据对象之一绑定到 UI 并且数据对象的属性发生更改时,UI 会自动更新。

可观察字段

如果您的类只有几个属性,那么创建实现 Observable 接口的类可能不值得。在这种情况下,您可以使用通用 Observable 类以及以下特定于原语的类来使字段可观察

可观察字段是自包含的可观察对象,它们具有单个字段。原始版本避免在访问操作期间进行装箱和拆箱。要使用此机制,请在 Java 编程语言中创建一个 public final 属性,或在 Kotlin 中创建一个只读属性,如下例所示

Kotlin

class User {
    val firstName = ObservableField<String>()
    val lastName = ObservableField<String>()
    val age = ObservableInt()
}

Java

private static class User {
    public final ObservableField<String> firstName = new ObservableField<>();
    public final ObservableField<String> lastName = new ObservableField<>();
    public final ObservableInt age = new ObservableInt();
}

要访问字段值,请使用 set()get() 访问器方法,或使用 Kotlin 属性语法

Kotlin

user.firstName = "Google"
val age = user.age

Java

user.firstName.set("Google");
int age = user.age.get();

可观察集合

某些应用使用动态结构来保存数据。可观察集合允许通过使用键访问这些结构。当键是引用类型(例如 String)时,ObservableArrayMap 类很有用,如下例所示

Kotlin

ObservableArrayMap<String, Any>().apply {
    put("firstName", "Google")
    put("lastName", "Inc.")
    put("age", 17)
}

Java

ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);

在布局中,您可以使用字符串键找到映射,如下例所示

<data>
    <import type="android.databinding.ObservableMap"/>
    <variable name="user" type="ObservableMap&lt;String, Object&gt;"/>
</data>
…
<TextView
    android:text="@{user.lastName}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
<TextView
    android:text="@{String.valueOf(1 + (Integer)user.age)}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

当键是整数时,ObservableArrayList 类很有用,如下所示

Kotlin

ObservableArrayList<Any>().apply {
    add("Google")
    add("Inc.")
    add(17)
}

Java

ObservableArrayList<Object> user = new ObservableArrayList<>();
user.add("Google");
user.add("Inc.");
user.add(17);

在布局中,您可以通过索引访问列表,如下例所示

<data>
    <import type="android.databinding.ObservableList"/>
    <import type="com.example.my.app.Fields"/>
    <variable name="user" type="ObservableList&lt;Object&gt;"/>
</data>
…
<TextView
    android:text='@{user[Fields.LAST_NAME]}'
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
<TextView
    android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

可观察对象

实现 Observable 接口的类允许注册希望收到来自可观察对象的属性更改通知的侦听器。

Observable 接口有一种机制来添加和删除侦听器,但您决定何时发送通知。为了简化开发,数据绑定库提供了 BaseObservable 类,该类实现了侦听器注册机制。实现 BaseObservable 的数据类负责在属性更改时发出通知。为此,请为 getter 分配 Bindable 注解,并在 setter 中调用 notifyPropertyChanged() 方法,如下例所示

Kotlin

class User : BaseObservable() {

    @get:Bindable
    var firstName: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.firstName)
        }

    @get:Bindable
    var lastName: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.lastName)
        }
}

Java

private static class User extends BaseObservable {
    private String firstName;
    private String lastName;

    @Bindable
    public String getFirstName() {
        return this.firstName;
    }

    @Bindable
    public String getLastName() {
        return this.lastName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
        notifyPropertyChanged(BR.firstName);
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
        notifyPropertyChanged(BR.lastName);
    }
}

数据绑定在模块包中生成一个名为 BR 的类,其中包含用于数据绑定的资源的 ID。Bindable 注解在编译期间在 BR 类文件中生成一个条目。如果数据类的基类无法更改,则可以使用 PropertyChangeRegistry 对象来有效地注册和通知侦听器,从而实现 Observable 接口。

生命周期感知对象

应用程序中的布局还可以绑定到数据绑定源,这些源会自动通知 UI 数据的变化。这样,您的绑定就会感知生命周期,并且仅在 UI 在屏幕上可见时才会触发。

数据绑定支持StateFlowLiveData。有关在数据绑定中使用LiveData 的更多信息,请参阅使用 LiveData 通知 UI 数据更改

使用 StateFlow

如果您的应用使用 Kotlin 和协程,则可以使用StateFlow 对象作为数据绑定源。要将StateFlow 对象与您的绑定类一起使用,请指定一个生命周期所有者来定义StateFlow 对象的作用域。以下示例在绑定类实例化后指定活动作为生命周期所有者

class ViewModelActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // Inflate view and obtain an instance of the binding class.
        val binding: UserBinding = DataBindingUtil.setContentView(this, R.layout.user)

        // Specify the current activity as the lifecycle owner.
        binding.lifecycleOwner = this
    }
}

将布局视图绑定到架构组件中所述,数据绑定与ViewModel 对象无缝协作。您可以按如下方式将StateFlowViewModel 结合使用

class ScheduleViewModel : ViewModel() {

    private val _username = MutableStateFlow<String>("")
    val username: StateFlow<String> = _username

    init {
        viewModelScope.launch {
            _username.value = Repository.loadUserName()
        }
    }
}

在您的布局中,使用绑定表达式将ViewModel 对象的属性和方法分配给相应的视图,如以下示例所示

<TextView
    android:id="@+id/name"
    android:text="@{viewmodel.username}" />

每当用户姓名值更改时,UI 都会自动更新。

禁用 StateFlow 支持

对于使用 Kotlin 和 AndroidX 的应用,数据绑定会自动包含StateFlow 支持。这意味着如果您的应用中尚不存在协程依赖项,则会自动将其包含在内。

您可以通过在build.gradle 文件中添加以下内容来选择退出此功能

Groovy

android {
    ...
    dataBinding {
        addKtx = false
    }
}

Kotlin

android {
    ...
    dataBinding {
        addKtx = false
    }
}

或者,您可以通过在gradle.properties 文件中添加以下行来全局禁用项目中的StateFlow

Groovy

android.defaults.databinding.addKtx = false

Kotlin

android.defaults.databinding.addKtx = false

其他资源

要了解有关数据绑定的更多信息,请参阅以下其他资源

示例

Codelabs

博文