Mvvm相关知识小结(DataBinding,ViewModel和LiveData的使用)

文章目录

    • DataBinding库
      • DataBinding的配置
      • 布局和绑定表达式
      • 双向数据绑定
      • 绑定数据
      • 事件处理
    • ViewModel
      • 导入ViewModel
      • ViewModel的创建和使用
    • LiveData
      • 概述
      • LiveData的创建
      • 观察 LiveData 对象
      • 更新 LiveData 对象
    • 参考资料

DataBinding库

数据绑定库是一种支持库,借助该库,可以使用声明性格式(而非程序化地)将布局中的界面组件绑定到应用中的数据源。

DataBinding的配置

在Module的build.gradle android模块中添加如下配置

android {
 dataBinding {
    enabled = true
 }
}

同步时出现Cause: unable to find valid certification path to requested target错误的解决办法:在项目的build.gradle的buildscript的repositories下加入:

maven { url "http://jcenter.bintray.com"}

再在allprojects的repositories下加入

mavenCentral()
jcenter{ url "http://jcenter.bintray.com/" }
maven { url "https://jitpack.io" }

布局和绑定表达式

<layout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto">	//根节点变为layout,并且它不能指定长和宽等属性,只能声明命名空间,否则在构建时会报错
    <data>			//data节点作用是连接 View 和 Modle 的桥梁
        <variable
            name="viewmodel"
            type="com.myapp.data.ViewModel" />	//bean实体类
    </data>
        
    <LinearLayout	 //UI布局的根布局
        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="@{viewmodel.name}" />

        <!--注意:这里age是int类型,必须转化为String,否则会运行时异常-->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(viewmodel.age)}" />
    </LinearLayout>
</layout>

可以在表达式语言中使用字符串连接运算符(即在@{…}中使用),逻辑和算术运算符等常用的运算符和关键字

双向数据绑定

上面的绑定为单向绑定(当ViewModel中的数据发生改变时,View相应的自动改变),而双向数据绑定多的一向是在用户行为对View发生影响时,ViewModel接收到View的改变并对其作出反应。

示例:

    <CheckBox
        android:id="@+id/rememberMeCheckBox"
        android:checked="@={viewmodel.rememberMe}"
    />

**注意:**双向数据绑定(@={})对于单向(@{})的区别是多了一个 =

为了应对数据的更改,可以将布局变量设置为 Observable(通常为 BaseObservable) 的实现,并使用 @Bindable 注释get方法,如以下代码段所示:

    public class LoginViewModel extends BaseObservable {
        // private Model data = ...
        @Bindable
        public Boolean getRememberMe() {	//VM->View VM中数据的改变引起View的改变
            return data.rememberMe;
        }
        public void setRememberMe(Boolean value) {	//View->VM View的改变引起VM中数据的改变
            // Avoids infinite loops.
            if (data.rememberMe != value) {
                data.rememberMe = value;
                // React to the change.
                saveData();
                // Notify observers of a new value.
                notifyPropertyChanged(BR.remember_me);
            }
        }
    }

绑定数据

系统会为每个布局文件生成一个绑定类。默认情况下,类名称基于布局文件的名称,它会转换为 Pascal 大小写形式并在末尾添加 Binding 后缀。以上布局文件名为 activity_main.xml,因此生成的对应类为 ActivityMainBinding。此类包含从布局属性(例如,user 变量)到布局视图的所有绑定,并且知道如何为绑定表达式指定值。建议的绑定创建方法是在扩充布局时创建,如以下示例所示:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
       User user = new User("Test", "User");
       binding.setUser(user);
    }

事件处理

通过数据绑定,可以编写从视图分派的表达式处理事件(例如,onClick() 方法)。事件特性名称由监听器方法的名称确定,但有一些例外情况。例如,View.OnClickListener 有一个 onClick() 方法,所以该事件的特性为 android:onClick

可以使用以下机制处理事件:

  • 方法引用:在表达式中,您可以引用符合监听器方法名称的方法(即和它名称相同的方法)。当表达式求值结果为方法引用时,数据绑定会将方法引用和所有者对象封装到监听器中,并在目标视图上设置该监听器。如果表达式的求值结果为 ,则数据绑定不会创建监听器,而是设置 监听器。

    例如在上面布局和绑定表达式的代码中的TextView中加入 android:onClick="@{() -> viewmodel.onLike()}" 属性

  • 监听器绑定:这些是在事件发生时进行求值的 lambda 表达式。数据绑定始终会创建一个要在视图上设置的监听器。事件被分派后,监听器会对 lambda 表达式进行求值。使用类似与方法引用,也可在 lambda 表达式中使用多个参数。

ViewModel

ViewModel是一个抽象类,旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续存在。在使用前需要声明相应的依赖。架构组件为界面控制器提供了ViewModel辅助程序类,该类负责为界面准备数据。 在配置更改期间会自动保留ViewModel对象,以便它们存储的数据立即可供下一个 Activity 或 Fragment 实例使用。

导入ViewModel

在Model - build.gradle添加依赖

implementation "android.arch.lifecycle:viewmodel:1.1.1"

ViewModel的创建和使用

    public class MyViewModel extends ViewModel {	//继承ViewModel抽象类
        private MutableLiveData<List<User>> users;
        public LiveData<List<User>> getUsers() {
            if (users == null) {
                users = new MutableLiveData<List<User>>();
                loadUsers();
            }
            return users;
        }
        private void loadUsers() {
            // Do an asynchronous operation to fetch users.	异步操作获取users
        }
    }

创建ViewModel对象后,就可以在活动中使用了

    public class MyActivity extends AppCompatActivity {
        public void onCreate(Bundle savedInstanceState) {
            //当系统首次调用活动的onCreate方法时创建一个ViewModel对象
            //重新创建了该 Activity,它接收的 MyViewModel 实例与第一个 Activity 创建的实例相同
            MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);	//通过这种方法获取ViewModel对象
            model.getUsers().observe(this, users -> {
                //更新UI
            });
        }
    }

LiveData

概述

LiveData是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

LiveData的创建

LiveData 是一种可用于任何数据的封装容器,其中包括可实现 Collections 的对象,如 List。LiveData对象通常存储在ViewModel对象中,并可通过 getter 方法进行访问,如以下示例中所示:

    public class NameViewModel extends ViewModel {//LiveData对象存储在ViewModel类中
    // Create a LiveData with a String
    private MutableLiveData<String> currentName;	//因为LiveData是一个抽象类,所以我们使用它的子类MutableLiveData
        public MutableLiveData<String> getCurrentName() {
            if (currentName == null) {
                currentName = new MutableLiveData<String>();
            }
            return currentName;
        }
    // Rest of the ViewModel...
    }

注意:确保将用于更新界面的LiveData对象存储在ViewModel对象中,而不是将其存储在 Activity 或 Fragment 中,原因如下:

  • 避免 Activity 和 Fragment 过于庞大。现在,这些界面控制器负责显示数据,但不负责存储数据状态。
  • 将 LiveData实例与特定的 Activity 或 Fragment 实例分离开,并使 对象在配置更改后继续存在

观察 LiveData 对象

应在活动的onCreate方法中观察LiveData,以确保 Activity 或 Fragment 变为活跃状态后具有可以立即显示的数据

    public class NameActivity extends AppCompatActivity {
        private NameViewModel model;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // Other code to setup the activity...
            //获取ViewModel对象
            model = ViewModelProviders.of(this).get(NameViewModel.class);
            //下面创建更新UI的观察者对象
            final Observer<String> nameObserver = new Observer<String>() {
                @Override
                public void onChanged(@Nullable final String newName) {
                    // Update the UI, in this case, a TextView.
                    nameTextView.setText(newName);
                }
            };
            //观察LiveData,传入作为LifecycleOwner的活动和observer.
            model.getCurrentName().observe(this, nameObserver);
        }
    }

更新 LiveData 对象

LiveData 没有公开可用的方法来更新存储的数据。MutableLiveData类将公开 setValue(T) 和 postValue(T) 方法,如果需要修改存储在LiveData对象中的值,则必须使用这些方法。通常情况下会在 ViewModel中使用 MutableLiveData,然后 ViewModel 只会向观察者公开不可变的 LiveData对象。

注意:必须调用 setValue(T) 方法以从主线程更新 LiveData 对象。如果在 worker 线程中执行代码,则可以改用 postValue(T) 方法来更新 LiveData 对象,即两个方法都可以用。

    button.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            String anotherName = "John Doe";
            model.getCurrentName().setValue(anotherName);
        }
    });

在本例中调用 setValue(T) 导致观察者使用值 John Doe 调用其 onChanged() 方法。在所有情况下,调用 setValue() 或 postValue() 都会触发观察者并更新界面。

参考资料

Android JetPack

你可能感兴趣的:(移动应用开发之路)