In the previous article introduced part of the use of the DataBinding framework and THE theory of MVVM pattern, so today through a Demo to explain how the structure of MVVM in practice, and its specific use, the following together, about the DataBinding and MVVM, or first post the learning address.

DataBinding Series (1) : Introduction to DataBinding



Android MVVM mode understanding

Previous studies of DataBinding, and of course most tutorials on MVVM and DataBinding on the Web, have introduced a lot of variables into XML and then bound the data of those variables to the controls, making the XML very unreadable. The right thing to do, in fact, is just to introduce the ViewModel variable. And many of them don’t explain how to use the ViewModel.

rendering


Overall architecture MVVM, network request is retroFIT2 + RXJAVA2, picture loading Glide, list using xRecyclerView library

DataBinding is a tool that implements MVVM. It is important in an MVVM project, but I want to explain the role of each layer again

1.View layer is to display the data, and received user operations passed to the viewModel layer, through dataBinding data and View one-way binding or bidirectional binding






The layout of MainActivity is simple, just a list

<? The 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"  xmlns:tools="http://schemas.android.com/tools"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.jcodecraeer.xrecyclerview.XRecyclerView android:id="@+id/news_rv" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> </layout>Copy the code

2.MainActivity

public class MainActivity extends AppCompatActivity implements XRecyclerView.LoadingListener { 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); initRecyclerView(); newsVM = new NewsVM(this, binding, newsAdapter); Private void RecyclerView(); private void RecyclerView(); binding.newsRv.setRefreshProgressStyle(ProgressStyle.BallClipRotate); / / set the drop-down refresh style binding. NewsRv. SetLoadingMoreProgressStyle (ProgressStyle. BallClipRotate); / / set the tensile load more style binding. NewsRv. SetArrowImageView (R.m ipmap. 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() {// drop down to refresh newsvm.loadrefreshData (); } @override public void onLoadMore() {// add newsvm. loadMoreData(); }}Copy the code

Here is to do the initialization of RecyclerView, and set the refresh and loading style of XRecyclerView and callback, and is to create the corresponding ViewModel object, you can complete some operations through this object. The Activity here is basically a pure View, because it really only does uI-related work.

3. The Model layer gets the parsed network data and calls back to the ViewModel through the interface

public class NewsModelImpl implements INewsModel { private static final String TAG = "NewsModelImpl"; @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: "); loadListener.loadSuccess(simpleNewsBeanList); } } } @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: "); loadListener.loadComplete(); }}Copy the code

4.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(); } / data first to get news * * * * / private void getNewsData () {loadType = MainConstant. LoadData. FIRST_LOAD; mNewsModel.loadNewsData(currPage, this); } / * * * to get the drop-down REFRESH data * / public void loadRefreshData () {loadType = MainConstant. LoadData. REFRESH; currPage = 1; mNewsModel.loadNewsData(currPage, this); } / tensile load more data on * * * for * / 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(); }}Copy the code

The objects held in the ViewModel are the View and Mode interfaces that handle the business logic, not the Databing object, and the specific operations on the UI should remain in the View layer.
5.Adapter

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 void clickDianZan(simpleNewsBean, simpleNewsBean, simpleNewsBean)  int position) { if (simpleNewsBean.isGood.get()) { simpleNewsBean.isGood.set(false); Toastutils. show(mContext, "unlike position=" + position); toastutils. show(mContext," unlike position=" + position); } else { simpleNewsBean.isGood.set(true); Toastutils. show(mContext, "position=" + position); }}}Copy the code

It may be surprising to see that our onBindViewHolder() contains no UI updates, no setXX() pairs, just a few variables, and a click method.

6. Item_news, layout of items in a list

<? The 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" /> <! <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>Copy the code

Note:

1. The ImageView onclick method is implemented by lambda expression, its click event is the Adapter clickDianZan () method, which introduced several variables are set in adapter.

2. Since we did not get the binding type, we set it by calling setVariable (a,b). A represents: use the BR class to find the name defined by the attribute name in the VARIABLE tag in XML, and B represents: event or data. Of course, you can also implement a specific Binding based on the layout of the item, such as an ItemNewsBinding

3. User-defined attributes are implemented through BindingAdapter

<ImageView android:id="@+id/header_iv" android:layout_width="120dp" android:layout_height="60dp" app:imageUrl="@{simpleNewsBean.thumbnail}" /> <! <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 }" />Copy the code
Public class ImageHelper {/** * mv_vm XML passes in the URL to load the image * imageUrl for the name in the XML ** @param iv imageView * @param URL image path */ @BindingAdapter({"imageUrl"}) public static void loadImage(ImageView iv, String url) { Glide.with(iv.getContext()).load(url).into(iv); } /** * mv_vm XML set mipmap Resource ** @param iv imageView * @param resId Resource ID */ @bindingAdapter ({"resId"}) public static void loadMipmapResource(ImageView iv, int resId) { iv.setImageResource(resId); }}Copy the code



GitHub source code download

Scan the following TWO-DIMENSIONAL code or the public number search Android old bird development experience, you can follow [public number], continue to launch excellent articles