ViewModel的作用是为界面准备数据,数据的获取操作(如网络请求)或者一些数据处理可以在该类中编写方法,减少在Activity的代码。
ViewModel里面的数据可以分为以下三类。
- 普通数据,只是负责保存数据,无特别作用,可当做普通类来用
public class DataViewModel extends ViewModel { public String name; public int age; }
- 与LiveData结合使用。MutableLiveData的取/赋值方法分别为getValue(),setValue(),如果不在主线程,用postValue()。关于LiveData可以看我的另外一篇文章Android LiveData的使用
public class DataViewModel extends ViewModel { private MutableLiveData<String> name; private MutableLiveData<Integer> age; public MutableLiveData<String> getName() { if (name == null) { name = new MutableLiveData<String>(); //获取初始值的操作,否则默认为""。 } return name; } public MutableLiveData<Integer> getAge() { if (age == null) { age = new MutableLiveData<Integer>(); //获取初始值的操作,否则默认为0。 } return age; } }
- 与DataBinding结合使用,ObservableField自带get/set方法。DataBinding的使用可以看我另外一篇文章Android DataBinding的简单使用
/** * 什么时候用MutableLiveData?什么时候用ObservableField? * 双向绑定看起来很便利,不需关注值的变化去更新视图。但实际业务大多数要对输入的值进行校验并做一些处理,不仅仅只是把值赋给实体对象。 * 个人感觉用MutableLiveData比较灵活,有些时候控件是动态添加进布局里的,这种时候很难用ObservableField进行赋值(也很难进行逻辑处理), * 而使用MutableLiveData,在onChanged方法里面我想更新什么就更新什么(逻辑写在activity中思路也比较清晰) * 结论: * 1、在不用处理数据(即用户输入什么就是什么,不涉及逻辑处理)/只用做数据显示(并且页面布局都由XML写,不涉及动态添加控件)的情况下,使用ObservableField比较方便 * 2、其余情况用MutableLiveData比较灵活 */ public class DataViewModel extends ViewModel { public final ObservableField<String> name = new ObservableField<>(); public final ObservableField<Integer> age = new ObservableField<>(); }
这三种类型可以分开使用,也可以看需求混合搭配使用。官方提供了以下的创建方法来获取ViewModel的实体对象(我发布这篇文章时,中文官网展示的方法还是旧版的,以下的代码已经是修改后的最新方法,旧版方法已抛弃。无法保证以后版本更新会不会有新的创建方法,如以后发现代码报错,可去官网看最新代码)。
- 这种方法获取的ViewModel,仅在Activity第一次创建的时候会进行创建,之后将会复用之前创建好的ViewModel,可用于页面状态保存。
public class MyActivity extends AppCompatActivity { // Create a ViewModel the first time the system calls an activity's onCreate() method. // Re-created activities receive the same MyViewModel instance created by the first activity. public void onCreate(Bundle savedInstanceState) { ViewModelProvider.Factory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()); DataViewModel dataViewModel = new ViewModelProvider(this, factory).get(DataViewModel.class); } }
- 这种方法获取的ViewModel,每次都会进行重新创建(个人感觉跟直接用new DataViewModel()没什么不同,有不同观点的欢迎指出)。
public class MyActivity extends AppCompatActivity { public void onCreate(Bundle savedInstanceState) { ViewModelProvider.Factory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()); DataViewModel dataViewModel = factory.create(DataViewModel.class); } }
为了统一管理ViewModel,我写了下面这个工具类,所有ViewModel都可以通过这个类的方法来创建(也可以把这几个方法放在application的类里)。getPrivateViewModel()对应上面的方法1。getViewModel()对应上面的方法2,并存放在一个静态map中,可实现在多个页面公用同一个ViewModel的数据。
import android.app.Activity;
import android.app.Application;
import java.util.HashMap;
import java.util.Map;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
/**
* ViewModel工具类
*/
public class ViewModelUtils {
private static Map<Class, ViewModel> viewModelMap = new HashMap<>();
/**
* 获取全局唯一的ViewModel
* 常用与跨页面修改数据(并且需要刷新显示),比如在某个页面对该ViewModel里的MutableLiveData进行了observe,
* 无论在哪里修改ViewModel里面的MutableLiveData的值,这个页面都会收到通知(页面在活跃状态下马上收到,非活跃状态下将在变为活跃状态的那一刻收到),收到通知后调用onChanged()方法(一般是刷新视图)
* @param application 本项目所设置使用的application实体
* @param viewModelClass ViewModel对应的类
* @param <T>
* @return
*/
public static <T extends ViewModel> T getViewModel(Application application, Class<T> viewModelClass){
if (viewModelMap.containsKey(viewModelClass)){
return (T) viewModelMap.get(viewModelClass);
}
T t = ViewModelProvider.AndroidViewModelFactory.getInstance(application).create(viewModelClass);
viewModelMap.put(viewModelClass, t);
return t;
}
public static <T extends ViewModel> T getViewModel(Fragment fragment, Class<T> viewModelClass){
return getViewModel(fragment.getActivity(),viewModelClass);
}
public static <T extends ViewModel> T getViewModel(Activity activity, Class<T> viewModelClass){
return getViewModel(activity.getApplication(),viewModelClass);
}
/**
* 获取只在本页面唯一的ViewModel(一般用于保存页面状态,比如横竖屏切换后要恢复状态)
* @param application 本项目所设置使用的application实体
* @param viewModelClass ViewModel对应的类
* @param owner 一般就是AppCompatActivity或Fragment
* @return
*/
public static <T extends ViewModel> T getPrivateViewModel(Application application, Class<T> viewModelClass,ViewModelStoreOwner owner){
ViewModelProvider.Factory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
return new ViewModelProvider(owner, factory).get(viewModelClass);
}
public static <T extends ViewModel> T getPrivateViewModel(Activity activity, Class<T> viewModelClass,ViewModelStoreOwner owner){
return getPrivateViewModel(activity.getApplication(),viewModelClass,owner);
}
public static <T extends ViewModel> T getPrivateViewModel(Fragment fragment, Class<T> viewModelClass,ViewModelStoreOwner owner){
return getPrivateViewModel(fragment.getActivity(),viewModelClass,owner);
}
}