@Awille
2019-01-26T13:02:13.000000Z
字数 6622
阅读 106
设计模式 Android mvvm
在mvp设计模式当中,m是model,是数据层,在android开发中,主要是负责一些数据的更新工作,数据更新的方式或是网络获取或是数据库获取。v是view,主要是负责UI的显示与更新。P层是presenter,P层一般会持有view的实例,P层接受view层传入的信息去通知model层进行数据的更新,model层数据更新后,P层用持有的view实例调用相关方法进行view层的更新。在android开发当中,activity或者fragment一般会实现view层的方法,除了这些方法还会做一些UI的初始化与为各个控件添加监听器的工作。
上面提到activity跟fragment主要做的事情可以分为三部分:控件初始化、为控件设置监听器、实现mvp中view层的相关方法。这三部分中,实现view层的相关方法代码量一般来说在activity或者frament中占比是最大的,也正是这一部分代码,导致了activity或者fragment变得很臃肿,可能出现一个activity中有上千行代码的情况。
MVVM当中,以 VM(View Model) 代替了 MVP 的 P(Presenter)。MVP当中,M与V层被隔离,M与V直接通过P层进行交流。
View层接受用户数据后将信息传递给P层,P层直接读取或者更新再读取model中的数据,读取到的数据再对View层进行UI更改。这里出现的问题除了上面提到的造成Activity或者fragment臃肿以外,P层对model层的数据操作与对View层的更新都是基于接口的,所以当业务场景越来越多的时候,接口也会越来越多,这也是MVP模式开发的问题之一。而MVVM同样也可以解决这个问题。
在MVVM中,View层和ViewModel双向绑定。在android开发之中,这种view与viewmodel双向绑定的模式可以由dataBinding来实现。在mvvm中,viewmodel作用是创建关联,将model和view绑定起来,如此之后,我们model的更改,通过viewmodel反馈给view,从而自动刷新界面。双向绑定可以理解为View层可以向viewmodel发送数据,而viewmodel发生改变时可以直接更新view状态。viewmodel完全是用来做业务逻辑的,在mvvm中,model层的数据其实是已经具体绑定在view中的某个具体控件中的,所以viewmodel只要根据业务逻辑做好对model中data的更新就可以了。
DataBinding不用专门引入依赖,只需要在使用的模块中打开dataBinding的开关即可
android {...dataBinding {enabled = true;}}
model层数据实体类编写:
public class User implements Serializable {private String firstName;private String lastName;public User(String firstName, String lastName) {this.firstName = firstName;this.lastName = lastName;}public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName = firstName;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}}
编写view层的布局文件:
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><data><import type="com.example.databindingtest.model.User"/><variable name="user" type="User" /></data><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{user.firstName}"android:textSize="20sp"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{user.lastName}"android:textSize="25sp"/></LinearLayout></layout>
在编写上面的标签的内容的的时候,编译器可能会报这个错误
Attribute is missing the Android namespace prefix
这里要注意到最外层一定要是用layout包住的,其他linearLayout, relativeLayout都不行。另外dataBinding的使用对环境是有要求的,Android Studio版本在1.3以上 gradle的版本要在1.5.0-alpha1以上。目前IDE应该都是3.0版本以上的,报错一般是gradle版本过低造成的,升级一下gradle版本即可。gradle升级指南。
不过应该不会出现这个问题,现在一般gradle版本都在4.0版本以上了.
mainAcitivity中的代码:
public class MainActivity extends AppCompatActivity {private ActivityMainBinding binding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = DataBindingUtil.setContentView(this, R.layout.activity_main);User user = new User("Will", "Chung");binding.setUser(user);}}
以上是一个databinding的简单实用。以上的例子中我们看到了view层具体的控件直接与model层相应的数据直接绑定在一起。
在上面的简单demo中我们看到一种view与数据进行直接绑定的方式。但是上面的例子只是一个单向的绑定,binding.setUser(user)这行代码过后,当user对象发声改变时,view层不会相应做具体的更新。
正向绑定:当model层数据对象改变时,view层做出相应更新
反向绑定:当view层变化时,其绑定的数据对象做出相应的更新。
a、实体类继承BaseObservale类
b、Getter上使用注解@Bindable
c、在Setter里调用方法notifyPropertyChanged
package com.example.databindingtest.model;import android.databinding.BaseObservable;import android.databinding.Bindable;import com.android.databinding.library.baseAdapters.BR;import java.io.Serializable;public class User extends BaseObservable {private String name;public User(String name) {this.name = name;}@Bindablepublic String getName() {return name;}public void setName(String name) {this.name = name;notifyPropertyChanged(BR.name);}}
当get方法添加注解后,BR这个类中会自动添加name这个属性
package com.android.databinding.library.baseAdapters;public class BR {public static final int _all = 0;public static final int name = 1;public static final int user = 2;}
Data Binding还提供了一系列Observable,这样就不用每个绑定都创建obeservable类了
private static class User {public final ObservableField<String> firstName =new ObservableField<>();public final ObservableField<String> lastName =new ObservableField<>();public final ObservableInt age = new ObservableInt();}
java中访问方式为:
user.firstName.set("Google");int age = user.age.get();
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"><data><import type="com.example.databindingtest.model.User"/><variable name="user" type="User" /></data><LinearLayoutandroid:layout_height="match_parent"android:layout_width="match_parent"><EditTextandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@={user.name}"android:textSize="20sp"/></LinearLayout></layout>
随便弄一个自定义控件,比如继承view一个可以设置max属性的控件
public class RangeBar extends View {private int max;public RangeBar(Context context) {super(context);}public void setMax(int max){this.max = max;invalidate();}}
创建该属性值绑定的model层的实体类:
public class Range extends BaseObservable {int max;@Bindablepublic int getMax(){return max;}public void setMax(int max){this.max = max;notifyPropertyChanged(BR.max);}}
添加该控件
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><data><import type="com.example.databindingtest.model.RangeBar"/><variable name="range" type="RangeBar"/></data><LinearLayoutandroid:layout_height="match_parent"android:layout_width="match_parent"><com.example.databindingtest.model.RangeBarandroid:layout_width="wrap_content"android:layout_height="wrap_content"app:max ="@={range.max}"/></LinearLayout></layout>
在自定义view的上方添加反向绑定
@InverseBindingMethods({@InverseBindingMethod(type = com.example.databindingtest.model.Range.class,attribute = "max",event = "maxAttrChanged",method = "getMax")})public class RangeBar extends View {...}
设置监听器:该监听器主要是用来改变其对应类实例数据的值
private static InverseBindingListener maxInverseBindingListener;@BindingAdapter("maxAttrChanged")public static void setMaxChangedListener(RangeBar rangeBar,final InverseBindingListener bindingListener) {maxInverseBindingListener = bindingListener;}
查看该监听器:
public interface InverseBindingListener {/*** Notifies the data binding system that the attribute value has changed.*/void onChange();}
这里的onChange方法就是用来更新对用数据对象的值的,我们在需要的时候调用就可以了。