MVC:M-Model-模型、V-View-视图(xml)、C-Controller-控制器(activity),MVP作为MVC的演化版本,M-Model-模型、V-View-视图(activity)、P-Presenter-表示器。 从MVC和MVP两者结合来看,Controlller/Presenter在MVC/MVP中都起着逻辑控制处理的角色,起着控制各业务流程的作用。而 MVP与MVC最不同的一点是M与V是不直接关联的也是就Model与View不存在直接关系,这两者之间间隔着的是Presenter层,其负责调控 View与Model之间的间接交互,
1.MVVM模式分为Model,View,ViewModel 三个部分
View
View:界面层,对应于Activity,XML,View,负责数据显示以及用户交互。View还包括ViewDataBinding,
ViewModel
ViewModel:关联层,将Model和View进行绑定,持有view 和 model,负责业务逻辑处理。Model或者View更改时,实时刷新对方。。
Model
Model:数据层,包含数据实体和对数据实体的操作。
Binder绑定器
Android中的数据绑定技术由DataBinding和LiveData共同实现。当Activity/Fragment接收到来自ViewModel中的新数据时(由LiveData自动通知数据的改变),将这些数据通过DataBinding绑定到ViewDataBinding中,UI将会自动刷新。
MVVM的优势
1),使得M,V,VM的解耦更加彻底,在mvp模式中,p需要持有V的引用,才能去刷新UI,在MVVM模式中,View和Model使用databingding进行双向绑定,一方改变会直接通知另外一方,使得viewModel能专注于业务逻辑的处理,而不需要去关心UI刷新。
2),不会像MVC一样导致Activity中代码量巨大,也不会像MVP一样出现大量的View接口(Presente与View是通过接口进行交互的)。项目结构更加低耦合。
MVVM的劣势
1),数据绑定使得Bug很难被调试。
2),一个大的模块中,model也会很大,虽然使用方便了也很容易保证了数据的一致性,但是长期持有,不释放内存,就造成了花费更多的内存。
3),数据双向绑定不利于代码重用。客户端开发最常用的重用时View,但是数据双向绑定技术,让你在一个View都绑定了一个model,不同模块的model都不同,那就不能简单重用View了
总结
1.MVVM并不会提升程序的性能,甚至如果用不好还会降低性能。
2.MVVM会增大代码的总量。
3.阅读MVVM的代码你必须不停的跳来跳去,跳到你恶心想吐。
4.MVVM将让你的程序完全解耦。
Databinding框架
Databinding和MVVM的关系
MVVM是一种架构模式,DataBinding是一个实现数据和UI绑定的框架,是实现MVVM模式的工具。
引入DataBinding
在App的build.gradle添加
android{
.....
dataBinding {
enabled = true
}
}
固定写法
* 最外层用<layout>标签嵌套,注意layout的首字母是小写的“l”
* <layout>标签的下面紧跟着一个<data>标签,这个标签其实就是让我们进行数据绑定的一个标签
* <data>标签中,属性class databingView 名称包含着<variable>标签,这个标签就是我们将“变量”
放置的位置
* <variable>标签里面分别有<type> <name>两个标签,分别来标识变量类型和变量名称
* <type>标签 标识变量类型,比如java.lang.String这就是String类型,
com.guaju.mvvm.bean.User 这个就是一个我自定义的一个User类型
* <name>标签 表示的就是我们定义的一个变量名称,这个变量名称我们会在下方的布局和对应的java代码中引用到
使用。 建立数据模式
public class Author extends BaseObservable {
private String name;
private Date birthdate;
private boolean male;
@Bindable
public String getName() {return name;}
public void setName(String name) {this.name = name;}
@Bindable
public Date getBirthdate() {return birthdate;}
public void setBirthdate(Date birthdate) {this.birthdate = birthdate;}
@Bindable
public boolean isMale() { return male;}
public void setMale(boolean male) {this.male = male;}
-
新建一activity的布局文件,在layout节点中添加data定义,Rebuild Project,框架将自动生成AuthorBinding类,
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data class="AuthorBinding"> <variable name="vMode" type="cn.com.ssii.mvvm.book.viewmodel.AuthorViewModel" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{vMode.author.name}" /> </LinearLayout> </layout>
-
新建AuthorViewModel,并在xml文件中设置属性绑定
AuthorViewModelpublic class AuthorViewModel { private Activity activity; private ViewDataBinding binding; public Author author; public AuthorViewModel(Activity activity, ViewDataBinding binding) { author = new Author(); author.setBirthdate(new Date()); author.setMale(true); author.setName("Jim"); this.activity = activity; this.binding = binding; this.binding.setVariable(BR.vMode,this); } }
水电费水电费
-
activity:
public class AuthorActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ViewDataBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_author); AuthorViewModel viewModel = new AuthorViewModel(this, binding); } }
事件绑定
在使用mvvm后,不用在Activity中去findview,然后设置各的事件了,可能把事件定义到ViewMode或其它类中,在xml文件中定义对应变量并给事件绑定处理方法即可,如:
xml:<Button android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="@{vMode.buttonClick}" />
viewmodel:
public void buttonClick(View view) {
Toast.makeText(activity, "你点了我一下", Toast.LENGTH_SHORT).show();
}
参数必须是
View
,参数要对应于
setOnClickListener(onClickListener listener)
对应的
onClickListener
要实现的接口,即
public void onClick(View)
。其他的方法也一样
Fragment,和自定义View的使用
Fragment没有setContent方法,还有些情况为了适应平板和手机的需要我们会把一些业务抽象到一个View中,根据设备的大小再把View拼装到界面上来,所以我们的MVVM在View层还要支持View特别是继承ViewGroup然后添加一个从layout中inflate出来的一个View,这两种情况也同样可使用,方法如下
-
fragment
在fragment中,定义xml文件、model、modelview与之前说的内容是一样的,唯不同可能在实例化databinding的方式不一样,在fragment中我们通过下面语句来实例化一个databinding
public class AuthorFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.activity_author, container, false);
AuthorViewModel viewModel=new AuthorViewModel(getActivity(),binding);
return binding.getRoot();
}
}
- view定义xml文件、model、modelview与之前讲的内容一致,在view的构造函数中调用以下方法来实例化databinding
public class AuthorView extends FrameLayout {
public AuthorView(@NonNull Context context) {
this(context, null);
}
public AuthorView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, null, -1);
}
public AuthorView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(getContext()), R.layout.activity_author, this, true);
AuthorViewModel viewModel = new AuthorViewModel(context, binding);
}
}
属性双向绑定
@=
在属性绑定的时候使用@=的方式来双向绑定,使用了双向绑定,在EditText中输入内容,就会自动存储到对应的字段上,如果还有其它View单向绑定了这个值,它的显示也会发生变化
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={vModel.book.name}" />
Observable
一个纯的Java Model类在被修改后,UI并不会更新。如果我们想数据绑定后修改数据模型的值就可以更新UI怎么破呢?这里就要用到Obsservable了,当前还要配合@Bindable与notifyPropertyChanged一起使用,如下面这个数据模型被绑定到UI后,修改它的值,UI会自动更新。
public class Author extends BaseObservable {
private String name;
private Date birthdate;
private boolean male;
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
@Bindable
public Date getBirthdate() {
return birthdate;
}
public void setBirthdate(Date birthdate) {
this.birthdate = birthdate;
notifyPropertyChanged(BR.birthdate);
}
@Bindable
public boolean isMale() {
return male;
}
public void setMale(boolean male) {
this.male = male;
notifyPropertyChanged(BR.male);
}
}
ObservableField
如果一个Model里的的有字段都要示修改后自动更新UI,我们还向上面使用Obsservable肯定会产生更多代码,谁也不原写那么多代码是吧?那么我们只要把字段定义为ObservableField就OK了。ObservableField提供的类型有ObservableFile<T>, ObservableBoolean, ObservableByte, ObservableInt, ObservableChar, ObservableLong, ObservableFloat, ObserbableDouble, ObservableParcelable, 这些基本已经能满足所有数据类型的需要了。
/**
* Name:Book
* Description:
* Author:leix
* Time: 2017/4/5 10:32
*/
public class Book {
public final ObservableField<String> name = new ObservableField<>();
public final ObservableField<String> description = new ObservableField<>();
public final ObservableInt price = new ObservableInt();
public final ObservableField<Date> publishDate = new ObservableField<>();
}
在xml中的绑定与之前的字段绑定一样,只是在java中修改值和获取有些不同,java中访问如下:
book.name.get();//获取name属性值
book.name.set("Thinking in java");//设置name属性值
RecyclerView 的使用
这里介绍RecyclerView的使用,其它RecyclerView是使用适配器模式View的代表,讲了它的使用,ViewPager、Spiner这些控件的使用与它基本一致。
- 创建item的layout文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data class="RecyclerItemBinding">
<variable
name="author"
type="cn.com.ssii.mvvm.book.model.Author" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{author.name}" />
</LinearLayout>
</layout>
- 写Adapter
/**
* Name:AuthorAdapter
* Description:
* Author:leix
* Time: 2017/4/5 16:51
*/
public class AuthorAdapter extends RecyclerView.Adapter<AuthorAdapter.ViewHolder> {
List<Author> dataSet;
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.activity_author, null, false);
return new ViewHolder(binding);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.getBinding().setVariable(BR.author,dataSet.get(position));
}
@Override
public int getItemCount() {
return dataSet == null ? 0 : dataSet.size();
}
public static class ViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder {
protected T binding;
public ViewHolder(T binding) {
super(binding.getRoot());
this.binding = binding;
}
public T getBinding() {
return binding;
}
}
}
https://www.jianshu.com/p/fe30044d2bd2