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模式快速开发框架