1.MVC
Model View Controller模式,数据结构和Controller逻辑与UI进行解耦。
- View:视图层,XML,它的职责是负责显示从Controller上获取的数据,一般由Activity承担其处理;
- Controller:控制层,一般也是Activity处理的比较多,从模型层获取数据,然后将数据绑定到view上,还需要对用户行为(输入等)进行监听;
- Model:模型层,对数据库的操作、网络的操作、文件的操作等耗时操作都应该在model层进行处理,承担的一般是一些业务逻辑业务处理等。
2.MVP
Model,View and Presenter模式,是由MVC演变而来,将Controller层变成了Presenter。
相较于MVC,MVP中model层不再与view层进行直接通讯,Presenter起到了桥梁的作用,三者直接的关系:
1.View接收用户的交互请求
2. view将请求转发给Presenter
3. Presenter再转交给Model进行数据处理
4. 数据处理完后,Model再通过接口形式通知Presenter数据已经发生改变
5. Presenter层收到Model的通知后,更新至View层
Model:与MVC一样都是处理业务逻辑的
Viwe:一般是Activity、Fragment,它会持有Presenter的实例,来调用Presenter层的方法
Presenter:起到桥梁的作用,接收model层数据进而转交给View层处理
3.MVVM
随着业务逻辑的增加,UI层改变很多的情况下,View接口可能也会变得很庞大,如果能够实现数据与UI的双向绑定那该多好而MVVM就是解决这个问题的。
Model View ViewModel模式
- 用户直接跟View进行交互
- View和ViewModel一对多
- View有ViewModel的引用,但是ViewModel没有任何关联View的信息,也无法直接操作view,它们之间通过DataBinding进行双向数据绑定
从设计图上看其实跟MVP也差不多,其实差别还挺大,MVP中View和Presenter相互持有才能互相调用,而MVVM中View和ViewModel则是使用DataBinding进行绑定关联,使用起来更加的方便、解耦。
3.1DataBinding的使用
在app build.gradle中声明以下即可
dataBinding {
enabled = true
}
首先先对布局进行改造处理
<layout>
<import type=""/>//需导包时添加
<varible //变量参数
name=""
type="" />
<布局><布局/>
</layout>
接着进行rebuild操作,就会自动生成一个Binding类,生成结构为layout文件名的结构
MainMvvmBinding binding = DataBindingUtil.setContentView(this, R.layout.main_mvvm);
绑定数据格式为 ·@{" “},双向绑定@={” "}
目前支持双向绑定的控件如下
AbsListView android:selectedItemPosition
CalendarView android:date
CompoundButton android:checked
DatePicker android:year, android:month, android:day
NumberPicker android:value
RadioGroup android:checkedButton
RatingBar android:rating
SeekBar android:progress
TabHost android:currentTab
TextView android:text
TimePicker android:hour, android:minute
简单使用
效果
布局部分
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View" />
<variable
name="user"
type="com.wxx.androiddemo.bean.User" />
<variable
name="presenter"
type="com.wxx.androiddemo.databinding.MainActivity.Presenter" />
<variable
name="showTextView"
type="boolean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/user"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:hint="用户名"
android:onTextChanged="@{presenter.onTextChanged}"
android:text="@={user.name}" />
<EditText
android:id="@+id/psw"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="20dp"
android:hint="密码"
android:text="@={user.content}" />
<Button
android:id="@+id/btn_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:background="@drawable/press"
android:onClick="@{presenter.onClick}"
android:text="@{@string/app_name(user.name)}"
android:textColor="@color/colorWhite"
android:textSize="18sp" />
<Button
android:id="@+id/btn_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:background="@{@drawable/press}"
android:onClick="@{presenter.onClick}"
android:text='@{user.map["ite_2"]}'
android:textColor="@color/colorWhite"
android:textSize="18sp"
android:visibility="@{user.visable? View.GONE : View.VISIBLE}" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/isChecked"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选择了CheckBox我就隐身给你看!!!"
android:visibility="@{isChecked.checked?View.GONE:View.VISIBLE}" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onCheckedChanged="@{presenter.onCheckedChanged}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选择了CheckBox我就隐身给你看!!!"
android:visibility="@{showTextView?View.GONE:View.VISIBLE}" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:orientation="horizontal">
<Button
android:id="@+id/add"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/press"
android:onClick="@{presenter.onClick}"
android:text="增加"
android:textColor="@color/colorWhite" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@color/colorWhite" />
<Button
android:id="@+id/remove"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/press"
android:onClick="@{presenter.onClick}"
android:text="移除"
android:textColor="@color/colorWhite" />
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</layout>
public class User extends BaseObservable {
private int id;
private int age;
private String name = "default";
private String content = "xxx";
private String headImgUrl;
private ObservableBoolean isVisable;
public ObservableArrayMap<String, String> map = new ObservableArrayMap<>();
public ObservableArrayList<String> list = new ObservableArrayList<>();
public User( boolean isVisable, String name, String content) {
this.name = name;
this.content = content;
this.isVisable = new ObservableBoolean(isVisable);
}
public User(String name, String content) {
this.name = name;
this.content = content;
isVisable = new ObservableBoolean(false);
map.put("ite_1", "Click_1 参数");
map.put("ite_2", "Click_2 参数");
map.put("ite_3", "Click_3 参数");
list.add("Click_1");
list.add("Click_1");
list.add("Click_1");
}
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
@Bindable
public String getContent() {
return content;
}
………………
………………
}
也可以这样声明,就无需进行@Bindable了
public class BindingFile<T> {
public ObservableField<Object> name = new ObservableField<>();
public ObservableMap<Object, Object> map = new ObservableArrayMap<>();
public ObservableArrayList<T> list = new ObservableArrayList<>();
public ObservableBoolean isOk = new ObservableBoolean(false);
}
Activity
public class MainActivity extends AppCompatActivity {
User user = new User("test", "why");
MainBindingBinding binding;
MyAdapter mAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.main_binding);
binding.setUser(user);
binding.setPresenter(new Presenter());
binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new MyAdapter(this);
binding.recyclerView.setAdapter(mAdapter);
mAdapter.setmListener(new MyAdapter.OnItemClickListener() {
@Override
public void onUserClick(int position, User user) {
Toast.makeText(MainActivity.this, "" + position, Toast.LENGTH_SHORT).show();
}
});
//添加动画
binding.addOnRebindCallback(new OnRebindCallback() {
@Override
public boolean onPreBind(ViewDataBinding binding) {
ViewGroup root = (ViewGroup) binding.getRoot();
TransitionManager.beginDelayedTransition(root);
return true;
}
});
}
String[] languages = new String[]{"Android", "Java", "Html", "RN", "Python"};
String[] urls = new String[]{
"https://avatar.csdnimg.cn/2/4/7/1_wu996489865.jpg",
"https://avatars3.githubusercontent.com/u/15843806?s=400&u=717f48864122fbee961992272ab18665f3969718&v=4",
"https://error"
};
public class Presenter {
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_1:
int i = (int) (Math.random() * 1000 + 1);
user.setName(String.valueOf(i));
user.setIsVisable(!user.isVisable().get());
break;
case R.id.add:
boolean isVisable = new Random().nextBoolean();
String language = languages[new Random().nextInt(5)];
user = new User(isVisable, language, language + "是这个世界上最好用的语言!");
user.setHeadImgUrl(urls[new Random().nextInt(3)]);
mAdapter.add(user);
break;
case R.id.remove:
mAdapter.remove();
break;
case R.id.btn_2:
user.setContent("随机变化:" + System.currentTimeMillis());
break;
default:
break;
}
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
binding.user.setSelection(s.length());
}
/**
* CheckBox事件
*
* @param view .
* @param isChecked .
*/
public void onCheckedChanged(View view, boolean isChecked) {
binding.setShowTextView(isChecked);
}
}
}
mvvm Demo构建
效果图
首先是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">
<data>
<variable
name="articleViewModel"
type="com.wxx.androiddemo.databinding.mvvmdemo.ArticleViewModel" />
</data>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:refresh="@{articleViewModel.refreshing}">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:items="@{articleViewModel.items}" />
</android.support.v4.widget.SwipeRefreshLayout>
</layout>
item布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="article"
type="com.wxx.androiddemo.bean.ArticleBean.DataBean.DatasBean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorWhite"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="2"
android:padding="5dp"
android:text="@{article.title}"
android:textSize="18sp"
android:textColor="@color/colorBlack"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:ellipsize="end"
android:maxLength="10"
android:text="@{article.author}"
android:textColor="@color/colorGray" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:text="." />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:ellipsize="end"
android:maxLength="10"
android:text="@{article.superChapterName}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="3dp"
android:gravity="end"
android:text="@{article.publishTimeStr}" />
</LinearLayout>
</LinearLayout>
</layout>
Model层
model层其实就是网络数据的请求
public class ArticleModel {
private IArticle article;
private JsonRequest request;
private int what;
public void setArticle(IArticle article) {
this.article = article;
}
public void fetch(int what, int page, final RequestType type) {
this.what = what;
request = new JsonRequest(AppUtil.getUrl(page), RequestMethod.GET);
request.setCacheMode(CacheMode.REQUEST_NETWORK_FAILED_READ_CACHE);
CallServer.getHttpclient().add(what, request, new HttpListener<JSONObject>() {
@Override
public void success(int what, Response<JSONObject> response) {
ArticleBean articleBean = new Gson().fromJson(response.get().toJSONString(), ArticleBean.class);
if (article != null) {
article.onSuccess(type,articleBean);
}
}
@Override
public void fail(int what, String e) {
if (article != null) {
article.onFail(what, e);
}
}
});
}
public void cancel() {
if (request != null) {
CallServer.getHttpclient().cancelWhat(what);
}
}
}
ViewModel层
数据绑定处理
public class ArticleViewModel implements IArticle {
//数据源
public ObservableField<ArticleBean> items = new ObservableField<>();
//是否刷新
public ObservableBoolean refreshing = new ObservableBoolean(true);
private ArticleModel model;
public ArticleViewModel() {
model = new ArticleModel();
model.setArticle(this);
}
/**
*
* @param what 请求what
* @param page 页数
* @param type 请求类型
*/
public void fetch(int what, int page,RequestType type) {
refreshing.set(true);
model.fetch(what, page,type);
}
@Override
public void onSuccess(RequestType type, ArticleBean article) {
article.setType(type);
items.set(article);
refreshing.set(false);
}
@Override
public void onFail(int what, String errorMsg) {
refreshing.set(false);
}
}
Adapter适配器
public class ArticleAdapter extends RecyclerView.Adapter<BindingViewHolder> {
private LayoutInflater inflater;
private List<ArticleBean.DataBean.DatasBean> mList;
private Context mContext;
public ArticleAdapter(Context mContext) {
mList = new ArrayList<>();
this.mContext = mContext;
inflater = LayoutInflater.from(mContext);
}
@NonNull
@Override
public BindingViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.item_article, viewGroup, false);
return new BindingViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull BindingViewHolder bindingViewHolder, int i) {
ArticleBean.DataBean.DatasBean datasBean = mList.get(i);
datasBean.setPublishTimeStr(AppUtil.longTime2StringTime(datasBean.getPublishTime()));
//设置绑定数据
bindingViewHolder.getmBinding().setVariable(BR.article, datasBean);
//必须调用,当绑定的数据发生改变刷新视图
bindingViewHolder.getmBinding().executePendingBindings();
}
@Override
public int getItemCount() {
return mList != null ? mList.size() : 0;
}
public void addAll(List<ArticleBean.DataBean.DatasBean> list) {
mList.addAll(list);
notifyDataSetChanged();
}
public void loadRefresh(List<ArticleBean.DataBean.DatasBean> list) {
mList.clear();
mList.addAll(list);
notifyDataSetChanged();
}
}
接下来就是Activity
public class DemoMainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
private MainMvvmBinding binding;
private ArticleAdapter mAdapter;
private ArticleViewModel viewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.main_mvvm);
binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
binding.refresh.setOnRefreshListener(this);
mAdapter = new ArticleAdapter(getApplicationContext());
binding.recyclerView.setAdapter(mAdapter);
DividerItemDecoration decoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
binding.recyclerView.addItemDecoration(decoration);
viewModel = new ArticleViewModel();
binding.setVariable(BR.articleViewModel, viewModel);
binding.refresh.post(new Runnable() {
@Override
public void run() {
viewModel.fetch(0, 0,RequestType.FIRST_LOAD);
}
});
}
@Override
public void onRefresh() {
viewModel.fetch(0, 0,RequestType.DOWN_REFRESH_LOAD);
}
}
可以看到视图层跟Model层实现了真正的解耦,而且数据源的变化我们也不用处理了都交给了DataBinding去处理。