MVVM
MVVM是Model-View-ViewModel的简写。
了解MVVM+data binding的开发模式。
所以学习之前一定要先学习Data Binding!
至于MVVM基本上和MVP一模一样,感觉只是名字替换了一下。他的关键技术就是今天的主题(Data Binding)。View的变化可以自动的反应在ViewModel,ViewModel的数据变化也会自动反应到View上。这样开发者就不用处理接收事件和View更新的工作,框架已经帮你做好了。
最近很火最主要的原因就是谷歌推出了data binding这个框架,可以轻松的实现MVVM。我个人觉得是Android往后发展的趋势,毕竟谷歌都推出了Datebinding,而使用Datebinding也就可以不用去使用bufferknife了
然后在对应的Module的build.gradle中添加
android { ......... dataBinding{ enabled=true } }
创建对象
public class User { private String firstName; private String 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; } }
布局
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <import type="com.example.asus.mvvmdemo.User"/> <variable name="user" type="User" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}" android:textSize="20sp" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.lastName}" android:textSize="25sp" /> </LinearLayout> </layout>
这里跟平时的布局有点不同,最外层是layout,里面分别是是data以及我们的布局。
data:声明了需要用到的user对象,type用于是定路径。
可以在TextView中的看到android:text="@{user.firstName}"
绑定数据
public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_main);activity_main binding = DataBindingUtil.setContentView(this, R.layout.activity_main); User user = new User("Micheal", "Jack"); binding.setUser(user); }
import com.example.asus.mvvmdemo.databinding.ActivityMainBinding;
问我ActivityMainBinding哪来的?我怎么知道...
ActivityMainBinding是根据布局文件的名字生成的,在后面加了Binding。
DataBindingUtil
绑定关系如下:xml和代码中这样绑定的。
binding.setUser(user); binding.setHandle(new MyHandler());
<data > <import type="com.example.gavin.databindingtest.User"/> <variable name="user" type="User" /> <variable name="handle" type="com.example.gavin.databindingtest.MyHandler"/>
总结:
上面的demo不是一个完整的,不是一个正确的mvvm的结构,都糅合在一起了。
参考:
http://www.cnblogs.com/longjunhao/p/5860353.html
1.View层就是展示数据的,以及接收到用户的操作传递给viewModel层,通过dataBinding实现数据与view的单向绑定或双向绑定
2.Model层最重要的作用就是获取数据了,当然不止于此,model层将结果通过接口的形式传递给viewModel层
3.ViewModel 层通过调用model层获取数据,以及业务逻辑的处理。
4.mvvm中 viewModel 和MVP中的presenter 的作用类似 ,只不过是通过 databinding 将数据与ui进行了绑定。
大型项目实战:
Model层:和MVp的Mode是一样的。
public interface INewsModel { /** * 获取新闻数据 * * @param page 页数 * @param loadListener */ void loadNewsData(int page, BaseLoadListener<SimpleNewsBean> loadListener); }
View 层:
public interface IBaseView { /** * 开始加载 * * @param loadType 加载的类型 0:第一次记载 1:下拉刷新 2:上拉加载更多 */ void loadStart(int loadType); /** * 加载完成 */ void loadComplete(); /** * 加载失败 * * @param message */ void loadFailure(String message); }
Model的实现类:
NewsModelImpl
public class NewsModelImpl implements INewsModel { private static final String TAG = "NewsModelImpl"; List<SimpleNewsBean> simpleNewsBeanList = new ArrayList<SimpleNewsBean>(); @Override public void loadNewsData(final int page, final BaseLoadListener<SimpleNewsBean> loadListener) { HttpUtils.getNewsData() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new DisposableObserver<NewsBean>() { @Override public void onNext(@NonNull NewsBean newsBean) { Log.i(TAG, "onNext: "); List<NewsBean.OthersBean> othersBeanList = newsBean.getOthers(); simpleNewsBeanList.clear(); if (othersBeanList != null && othersBeanList.size() > 0) { for (NewsBean.OthersBean othersBean : othersBeanList) { String thumbnail = othersBean.getThumbnail(); String name = othersBean.getName(); String description = othersBean.getDescription(); Log.i(TAG, "thumbnail:---->" + thumbnail); Log.i(TAG, "name:---->" + name); Log.i(TAG, "description:---->" + description); //构造Adapter所需的数据源 SimpleNewsBean simpleNewsBean = new SimpleNewsBean(); simpleNewsBean.thumbnail.set(thumbnail); simpleNewsBean.name.set(name); simpleNewsBean.description.set(description); simpleNewsBeanList.add(simpleNewsBean); if (page > 1) { //这个接口暂时没有分页的数据,这里为了模拟分页,通过取第1条数据作为分页的数据 break; } } } } @Override protected void onStart() { super.onStart(); Log.i(TAG, "onStart: "); loadListener.loadStart(); } @Override public void onError(@NonNull Throwable throwable) { Log.i(TAG, "onError: " + throwable.getMessage()); loadListener.loadFailure(throwable.getMessage()); } @Override public void onComplete() { Log.i(TAG, "onComplete: "); new Handler().postDelayed(new Runnable() { @Override public void run() { loadListener.loadSuccess(simpleNewsBeanList); loadListener.loadComplete(); } }, 2000); } }); }
View的xml:
<?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> <import type="com.zx.mvvmdemo.R" /> <variable name="simpleNewsBean" type="com.zx.mvvmdemo.bean.SimpleNewsBean" /> <variable name="adapter" type="com.zx.mvvmdemo.adapter.NewsAdapter" /> <variable name="position" type="int" /> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="15dp"> <ImageView android:id="@+id/header_iv" android:layout_width="120dp" android:layout_height="60dp" app:imageUrl="@{simpleNewsBean.thumbnail}" /> <!--标题--> <TextView android:id="@+id/title_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:layout_toEndOf="@id/header_iv" android:text="@{simpleNewsBean.name}" android:textColor="#000" android:textSize="16sp" /> <!--描述--> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignStart="@id/title_tv" android:layout_below="@id/title_tv" android:layout_marginTop="8dp" android:text="@{simpleNewsBean.description}" android:textSize="14sp" /> <!--点赞或者取消点赞,onClick()用的lambda表达式--> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_below="@id/header_iv" android:layout_marginEnd="15dp" android:layout_marginTop="8dp" android:onClick="@{()->adapter.clickDianZan(simpleNewsBean,position)}" app:resId="@{simpleNewsBean.isGood ? R.mipmap.dianzan_press : R.mipmap.dianzan_normal }" /> </RelativeLayout> </layout>
ViewModel:
public class NewsVM implements BaseLoadListener<SimpleNewsBean> { private static final String TAG = "NewsVM"; private INewsModel mNewsModel; private INewsView mNewsView; private NewsAdapter mAdapter; private int currPage = 1; //当前页数 private int loadType; //加载数据的类型 public NewsVM(INewsView mNewsView, NewsAdapter mAdapter) { this.mNewsView = mNewsView; this.mAdapter = mAdapter; mNewsModel = new NewsModelImpl(); getNewsData(); } /** * 第一次获取新闻数据 */ private void getNewsData() { loadType = MainConstant.LoadData.FIRST_LOAD; mNewsModel.loadNewsData(currPage, this); } /** * 获取下拉刷新的数据 */ public void loadRefreshData() { loadType = MainConstant.LoadData.REFRESH; currPage = 1; mNewsModel.loadNewsData(currPage, this); } /** * 获取上拉加载更多的数据 */ public void loadMoreData() { loadType = MainConstant.LoadData.LOAD_MORE; currPage++; mNewsModel.loadNewsData(currPage, this); } @Override public void loadSuccess(List<SimpleNewsBean> list) { if (currPage > 1) { //上拉加载的数据 mAdapter.loadMoreData(list); } else { //第一次加载或者下拉刷新的数据 mAdapter.refreshData(list); } } @Override public void loadFailure(String message) { // 加载失败后的提示 if (currPage > 1) { //加载失败需要回到加载之前的页数 currPage--; } mNewsView.loadFailure(message); } @Override public void loadStart() { mNewsView.loadStart(loadType); } @Override public void loadComplete() { mNewsView.loadComplete(); } }
public class NewsAdapter extends BaseAdapter<SimpleNewsBean, BaseViewHolder> { public NewsAdapter(Context context) { super(context); } @Override public BaseViewHolder onCreateVH(ViewGroup parent, int viewType) { ViewDataBinding dataBinding = DataBindingUtil.inflate(inflater, R.layout.item_news, parent, false); return new BaseViewHolder(dataBinding); } @Override public void onBindVH(BaseViewHolder baseViewHolder, int position) { ViewDataBinding binding = baseViewHolder.getBinding(); binding.setVariable(BR.simpleNewsBean, mList.get(position)); binding.setVariable(BR.position,position); binding.setVariable(BR.adapter,this); binding.executePendingBindings(); //防止闪烁 }
public class MainActivity extends AppCompatActivity implements INewsView, XRecyclerView.LoadingListener { private Context mContext; private ActivityMainBinding binding; private NewsAdapter newsAdapter; //新闻列表的适配器 private NewsVM newsVM; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(this, R.layout.activity_main); mContext = this; initRecyclerView(); newsVM = new NewsVM(this, newsAdapter); } /** * 初始化RecyclerView */ private void initRecyclerView() { binding.newsRv.setRefreshProgressStyle(ProgressStyle.BallClipRotate); //设置下拉刷新的样式 binding.newsRv.setLoadingMoreProgressStyle(ProgressStyle.BallClipRotate); //设置上拉加载更多的样式 binding.newsRv.setArrowImageView(R.mipmap.pull_down_arrow); binding.newsRv.setLoadingListener(this); LinearLayoutManager layoutManager = new LinearLayoutManager(this); binding.newsRv.setLayoutManager(layoutManager); newsAdapter = new NewsAdapter(this); binding.newsRv.setAdapter(newsAdapter); } @Override public void onRefresh() { //下拉刷新 newsVM.loadRefreshData(); } @Override public void onLoadMore() { //上拉加载更多 newsVM.loadMoreData(); } @Override public void loadStart(int loadType) { if (loadType == FIRST_LOAD) { DialogHelper.getInstance().show(mContext, "加载中..."); } } @Override public void loadComplete() { DialogHelper.getInstance().close(); binding.newsRv.loadMoreComplete(); //结束加载 binding.newsRv.refreshComplete(); //结束刷新 } @Override public void loadFailure(String message) { DialogHelper.getInstance().close(); binding.newsRv.loadMoreComplete(); //结束加载 binding.newsRv.refreshComplete(); //结束刷新 ToastUtils.show(mContext, message); } }
bindingcollectionadapter
参考博客:
总结的不错Android DataBinding库(MVVM设计模式)
Android MVVM模式快速开发框架