文章目录
题外
Jetpack中是有一个ViewModel的,但是并不是MVVM中的ViewModel,它的本意似乎是一个用于保存Activity中的临时数据,避免在屏幕旋转中丢失,然后顺带有Jetpack中的核心,关于Life周期的处理。
但是虽然说不是指定用于MVVM架构,但是使用MVVM架构中的ViewModel用它是非常方便的!
因为本来ViewModel就是用来提供数据的绑定的嘛
然后现在刚好在搭建项目,所以记录下具体的搭建方法
参考资料:https://github.com/manas-chaudhari/android-mvvm
绑定Activity的VM 和 layout
解决办法:
1、想想平常我们是怎么绑定的
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
User user = new User("Test", "User");
binding.setUser(user);
}
2、抽象到基类之后,如何绑定的问题:
1、获得一个binging Class,绑定了类和XML
你看啊,我们不是没有一般的
setContentView(R.layout.activity_main);
这个语句了嘛,现在用怎么绑定activity和XML文件呢?就是:
MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
MainActivityBinding,我们知道这个binding类的名字是从Activity的对应layout的名字来的。那么我一个基类,怎么对应呢?
基类activity,就用父类的binding Class嘛,只要用上作为父类的bindingClass,java会自动帮我们转换的。
那么我们的各个binding Class的父类是什么呢?那就是:ViewDataBinding
来看官方文档:https://developer.android.com/reference/android/databinding/ViewDataBinding
2、binding绑定实体类,使用的方法
为什么要动态绑定
好的,我们现在有了正确的bindingClass,用它我们就能让Activity找到它自己对应的XML。但是我还需要把正确的实体类绑定在这个bindingClass上啊,不然怎么提供正确的数据?也就是:
binding.setUser(user);
我们知道这个setUser方法,是根据你的XML中定义来的,
<data>
<variable name="user" type="com.example.User"/>
</data>
但是我抽象到父类上,怎么弄啊?又不能一一对应每个data的名字。但是你想,dataBinding框架既然能根据定义的变量名字提供对应的方法,那肯定能得到对应的变量的名称对吧
想一想,而我们需要的绑定条件是:变量名、bindingClass、实体类。这三个我们都能取得,那么能够进行动态绑定了嘛?
有时候,我们无法知道确切的binding类,比如RecyclerView Adapter可以使用任意的layout,
所以我们的binding类需要动态生成。
我们需要在onBindViewHolder方法中给变量赋值,比如我们的layout中声明了一个item变量,
我们通过BindingHolder的getBinding返回一个binding对象,调用setVariable方法给item变量赋值
参考:https://appkfz.com/2015/07/09/android-data-binding-3/
有点让人看不懂哈,所以我来翻译一下:RecyclerView的每个item的值,是由itemList的位置提供的,也就是items.get(position)。它的每个Item的Layout也可以是各不相同的,咋整呢?动态绑定的方法:
在onBindViewHolder()方法里,
利用DataBinding动态绑定ViewDataBinding.setVariable(BR.itemP, ItemPresenter);为每个Item设置点击事件。
同时,数据也是同样在里面绑定的:setVariable(BR.data, mDatas.get(position))。
参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/1214/6857.html
另外这个BR是个啥,
BR.java:这个文件主要是给我们在XML文件中每个标签设置的variable以及被Bindable注解的属性添加一个静态的int类型的索引,在以后对这些variable进行操作的时候都可以使用这个索引id作为参数。
也就是:上面的user变量的这个variable标签
参考:https://juejin.im/post/58400839ac502e006cbd0f31#heading-14
<data>
<variable name="user" type="com.example.User"/>
</data>
完成动态绑定
所以我们这里的父类也是一样,用这种动态绑定的方法来完成。
binding = DataBindingUtil.setContentView(this, getLayoutId());
Preconditions.checkNotNull(binding, "bindingClass in " + MvvmActivity.this.getClass().getSimpleName());
binding.setVariable(BR.vm, createViewModel());
在其他地方的动态绑定
但是,我们之前也说了,在使用RecyclerView的时候,我们也需要动态绑定,可惜RecyclerViewAdapter并不知道activity、layout,所以我们需要多来一层,使用一个ViewModelBinder,以后把这个ViewHolderBindre传入Adapter中就可以了
public interface ViewModelBinder {
void bind(ViewDataBinding viewDataBinding, ViewModel viewModel);
}
/*
通过完成这个bind方法,以后我们在activity中对RecyclerViewAdapter传入getDefaultBinder()方法,就可以得到每个
不同的activity的Binder了。
*/
@NonNull
public static final ViewModelBinder defaultBinder = new ViewModelBinder() {
@Override
public void bind(ViewDataBinding viewDataBinding, ViewModel viewModel) {
viewDataBinding.setVariable(BR.vm, viewModel);
}
};
private ViewDataBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, getLayoutId());
getDefaultBinder().bind(binding, createViewModel());
}
ViewModel传递消息
思路:一切无法在这个类本身持有的引用、完成的api调用,都可以通过传递一个控制器来解决。
ViewModel传递消息就是这样,ViewModel不应该持有Activity的引用,所以我们在构建自己的ViewModel的时候,可以通过构造函数传递一个Navigator类,用这个类来完成跳转