文章目录

  • 使用LiveData通知UI有关数据更改
  • 使用ViewModel管理与UI相关的数据
  • 使用可观察的ViewModel可以更好地控制绑定适配器

AndroidX 这个库中包含了

架构组件,我们可以使用

架构组件来设计更强大、可测试和可维护的应用程序。DataBinding库可以与架构组件无缝协作,进一步简化 UI 的开发,应用程序中的布局可以绑定到架构组件的数据中,这些数据已经帮助我们管理了 UI 控制器(Activity 和 Fragment)的生命周期并可以在数据变更时通知 UI。

这篇文章将介绍如何将架构组件整合到我们的应用程序中,以进一步增强使用数据绑定的好处。

使用LiveData通知UI有关数据更改


我们可以使用 LiveData 对象作为数据绑定源,在数据发生变化时自动通知 UI。

与实现 Observable 的对象(如可观察字段)不同,LiveData 对象知道订阅数据更改的观察者的生命周期。在Android Studio 3.1及更高版本中,我们可以在数据绑定代码中用 LiveData 对象替换 可观察字段。

要想在绑定类中使用 LiveData 对象,我们需要指定生命周期所有者来定义LiveData 对象的范围。以下示例在绑定类实例化后指定 activity 作为生命周期所有者:

class ViewModelActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Inflate view and obtain an instance of the binding class.
        UserBinding binding = DataBindingUtil.setContentView(this, R.layout.user);

        // Specify the current activity as the lifecycle owner.
        binding.setLifecycleOwner(this);
    }
}

我们还可以使用 ViewModel 组件(如 使用ViewModel管理与UI相关的数据 章节所述)将数据绑定到布局。在 ViewModel 组件中,我们可以使用 LiveData 对象来转换数据或合并多个数据源。以下示例显示如何转换 ViewModel 中的数据:

class ScheduleViewModel extends ViewModel {
    LiveData username;

    public ScheduleViewModel() {
        String result = Repository.userName;
        userName = Transformations.map(result, result -> result.value);
}

使用ViewModel管理与UI相关的数据


DataBinding 库可以与 ViewModel 组件无缝协作,这样能够公开布局观察到的数据并对其变化做出响应。将 ViewModel 组件与 DataBinding 库配合使用,我们可以将 UI 逻辑移出布局并放入易于测试的组件中。DataBinding 库确保 View 在需要时从 数据源 绑定和解除绑定,剩下的大部分工作就是确保你公开正确的数据。

要将 ViewModel 组件与DataBinding 库一起使用,我们必须实例化我们的 ViewModel 组件,该组件从 ViewModel 类继承,获取绑定类的实例,并将 ViewModel 组件分配给绑定类中的一个属性。以下示例显示如何在库中使用该组件:

class ViewModelActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Obtain the ViewModel component.
        UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);

        // Inflate view and obtain an instance of the binding class.
        UserBinding binding = DataBindingUtil.setContentView(this, R.layout.user);

        // Assign the component to a property in the binding class.
        binding.viewmodel = userModel;
    }
}

在布局中,使用绑定表达式将 ViewModel 组件的属性和方法分配给相应的 View,如下例所示:

<CheckBox
    android:id="@+id/rememberMeCheckBox"
    android:checked="@{viewmodel.rememberMe}"
    android:onCheckedChanged="@{() -> viewmodel.rememberMeChanged()}" />

使用可观察的ViewModel可以更好地控制绑定适配器

我们可以使用实现了ObservableViewModel 组件来通知其他应用组件有关数据的更改,类似于使用 LiveData 对象。

在有些情况下,即使失去了 LiveData 的生命周期管理功能,我们也更愿意使用实现了 Observable 接口的 ViewModel 组件而不是使用 LiveData 对象。使用实现 ObservableViewModel 组件可以更好地控制应用中的绑定适配器。例如,这种模式可以让我们在数据更改时更好地控制通知,还可以指定自定义方法来设置双向数据绑定中的属性值。

要实现可观察的 ViewModel 组件,我们必须创建一个继承了 ViewModel 类并实现 Observable 接口的类。当观察者使用addOnPropertyChangedCallback()removeOnPropertyChangedCallback() 方法订阅或取消订阅通知时,可以提供自定义的逻辑,我们也可以提供当 notifyPropertyChanged() 方法中的属性更改时运行的自定义逻辑。以下代码演示如何实现可观察的 ViewModel

/**
 * A ViewModel that is also an Observable,
 * to be used with the Data Binding Library.
 */
class ObservableViewModel extends ViewModel implements Observable {
    private PropertyChangeRegistry callbacks = new PropertyChangeRegistry();

    @Override
    protected void addOnPropertyChangedCallback(
            Observable.OnPropertyChangedCallback callback) {
        callbacks.add(callback);
    }

    @Override
    protected void removeOnPropertyChangedCallback(
            Observable.OnPropertyChangedCallback callback) {
        callbacks.remove(callback);
    }

    /**
     * Notifies observers that all properties of this instance have changed.
     */
    void notifyChange() {
        callbacks.notifyCallbacks(this, 0, null);
    }

    /**
     * Notifies observers that a specific property has changed. The getter for the
     * property that changes should be marked with the @Bindable annotation to
     * generate a field in the BR class to be used as the fieldId parameter.
     *
     * @param fieldId The generated BR id for the Bindable field.
     */
    void notifyPropertyChanged(int fieldId) {
        callbacks.notifyCallbacks(this, fieldId, null);
    }
}