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的结构,都糅合在一起了。
参考:
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模式快速开发框架