Mvp與Mvvm已經(jīng)出現(xiàn)很久了亡容,但是還有很多開發(fā)者沒有運用到自己的項目中忍疾。正好最近工作不是很忙沸毁,這兩天空余時間就寫了個demo问窃,來給還沒有運用到實戰(zhàn)項目的小伙伴一個參考亥鬓,如果有什么問題可以直接給我留言,Ok我們進入正題~
先看一下效果圖(由于平臺上傳gif圖有大小限制域庇,所以就分成兩個gif上傳的)
先簡單介紹一下demo
為了方便嵌戈,demo中下拉刷新上拉加載直接用的xRecyclerView庫覆积。列表類似于新聞客戶端多布局模式(無圖-1張圖-多圖)。以及app里非常常見的點贊功能熟呛,分別在mvp設計模式與mvvm設計模式中實現(xiàn)宽档。無論是mvp還是mvvm或其他的設計模式,建議都不要生搬硬套庵朝,靈活的運用和多變的實現(xiàn)才是掌握一種設計思想的最高境界(當然前提是不能脫離所運用的模式)吗冤。
包結(jié)構(gòu)
mvp與mvvm分為兩個包,看代碼的時候可以分開看偿短,common 里邊是簡單封裝的BaseAdapter欣孤,感興趣的可以下載源碼看看馋没,這里就不貼代碼了昔逗。下面的講解中,每一個功能點對應的mvp實現(xiàn)方式與mvvm實現(xiàn)方式我會對照著來講篷朵。本篇文章只針對demo中所涉及到的知識點來講勾怒。
ok到了我們最重要的部分
mvp與mvvm都是非常棒的設計模式,但是都各有利弊声旺,在這里我就不長篇大論的去墨跡了笔链,相信你們已經(jīng)多少有點了解了。
先來說一下Mvp
從上面的mvp.jpg圖中我們可以看到腮猖,mvp分為三層鉴扫,分別為Model、View澈缺、Presenter坪创。
1.view層調(diào)用presenter。
2.presenter收到view的信號姐赡,調(diào)用具體的業(yè)務邏輯model層莱预。
3.model層收到了presenter的信號,開始進行具體的業(yè)務邏輯處理项滑。
比如(網(wǎng)絡請求-數(shù)據(jù)庫存取...)
4.model層 將具體的業(yè)務邏輯處理完之后通過接口回調(diào)的形式
將結(jié)果返回給presenter依沮。
5.presenter收到model返回的結(jié)果同樣以接口的形式返回給
view層做相應的顯示
Mvvm
從上面的mvvm.jpg圖中我們可以看到,mvvm也是分為三層枪狂,分別為Model危喉、View、ViewModel州疾。
說到mvvm設計模式辜限,就要涉及到一個庫 databinding,
在AS中 使用起來也是比較方便的
直接在module:app中的 build.gradle中打開databinding的支持就可以了
android {
....
dataBinding {
enabled = true
}
}
具體的詳解可以看google的開發(fā)文檔(demo中所涉及到的databinding的知識點我會講解)
<a >Data Binding</a>
1.view層接收到用戶的操作傳遞給viewModel層
(View層與ViewModel 通過dataBinding
實現(xiàn)數(shù)據(jù)與view的單向綁定或雙向綁定)
2.ViewModel 層接收到用戶傳來的信號孝治,調(diào)用model層列粪。
3.model層收到viewModel的信號進行具體的業(yè)務邏輯處理审磁。
4.model層將結(jié)果通過接口的形式傳遞給viewModel層
5.因為viewModel與view通過databinding實現(xiàn)了綁定,viewModel接收到model傳來的結(jié)果做出相應的view展示
通過上邊的大白話描述~ 我們可以看到 mvp中 presenter 起到了橋接的作用岂座,view 層 與model層的通訊 通過中間量presenter 以接口的形式進行傳遞态蒂。mvvm中 viewModel 和presenter 的作用類似 ,只不過是通過 databinding 將數(shù)據(jù)與ui進行了綁定费什。
mvvm中 xml 對應的格式
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="name"
type="type"/>
</data>
<ViewGroup></ViewGroup>
</layout>
與以往的xml形式不同钾恢。最外層為 layout,里邊有data標簽 鸳址,data標簽里有variable變量屬性 瘩蚪。ViewGroup標簽中的內(nèi)容才和我們之前的xml是一樣的。
所有以layout為根標簽的布局都會生成對應的ViewDataBinding 類稿黍,比如 MainActivity的xml名字為 activity_main ,databinding就會自動為我們生成 ActivityMainBinding疹瘦。
獲取ActivityMainBinding的方法
在MainActivity 的oncreate方法中獲取
ActivityMainDatabinding
ActivityMainBinding binding =
DataBindingUtil.setContentView
(this, R.layout.activity_main);
之前設置布局文件的方式為setContentView(layoutId)
用databinding設置布局文件統(tǒng)一通過DataBindingUtil類來設置
下面看一下demo中具體的代碼實現(xiàn)
mvvm中BaseActivity的實現(xiàn)
從圖中可以看出 我們通過泛型 VDB 來指定ViewDatabinding的類型。
mvp中BaseActivity的實現(xiàn)
在mvp中我們依然通過泛型來指定IView接口 與 Presenter 的類型(方便在presenter中綁定Activity或Fragment的生命周期),presenter方便做attach 與 detach的操作巡球。
MvvmActivity中的代碼
/**
* Created by mj
* on 2017/5/22.
*/
public class MvVmActivity extends BaseMvVmActivity<ActivityMvVmBinding> implements XRecyclerView.LoadingListener {
/**
* 新聞列表 adapter
*/
NewsListAdapter adapter;
/**
* view model
*/
NewsListVm newsListVm;
@Override
public int getLayoutId() {
return R.layout.activity_mv_vm;
}
@Override
public void init(Bundle savedInstanceState) {
initAdapter();
initVM();
}
/**
* 初始化adapter
*/
private void initAdapter() {
XRecyclerView xRecyclerView = viewDataBinding.activityMvVmList;
xRecyclerView.setLoadingListener(this);
xRecyclerView.setLayoutManager(new LinearLayoutManager(context));
xRecyclerView.setArrowImageView(R.mipmap.pull_down_arrow);
xRecyclerView.setRefreshProgressStyle(ProgressStyle.BallClipRotate);
xRecyclerView.setLoadingMoreProgressStyle(ProgressStyle.BallClipRotate);
adapter = new NewsListAdapter();
xRecyclerView.setAdapter(adapter);
}
/**
* 初始化viewModel
*/
private void initVM() {
newsListVm = new NewsListVm(context,viewDataBinding, adapter);
}
@Override
public void onRefresh() {
newsListVm.setRefreshData();
}
@Override
public void onLoadMore() {
newsListVm.loadMoreData();
}
}
在mvvm中我們把用戶對view的操作以及UI的樣式 放在 activity中或xml中言沐,在上面的代碼中我們可以看到,下拉刷新和上拉加載以及 刷新的樣式 都放到Activity中酣栈,在下拉刷新和上拉加載的代碼中通過newsListVm (viewModel的實例)來調(diào)用刷新數(shù)據(jù)和加載數(shù)據(jù)
MvpActivity中的代碼
/**
* Created by mj
* on 2017/5/22.
*/
public class MvpActivity extends BaseMvpActivity<INewsListView, NewsListPresenterImpl> implements INewsListView, XRecyclerView.LoadingListener {
/**
* 首次加載
*/
public static final int FIRST_LOAD = 0;
/**
* 刷新
*/
public static final int REFRESH = 1;
/**
* 加載更多
*/
public static final int LOAD_MORE = 2;
/**
* recyclerView
*/
@BindView(R.id.mvp_list_view)
XRecyclerView recyclerView;
/**
* adapter
*/
NewsListAdapter adapter;
/**
* 頁數(shù)
*/
private int pageNum = 1;
@Override
public NewsListPresenterImpl initPresenter() {
return new NewsListPresenterImpl();
}
@Override
public int getLayoutId() {
return R.layout.activity_mvp;
}
@Override
public void init(Bundle savedInstanceState) {
initAdapter();
firstLoadData();
}
/**
* 初始化adapter
*/
private void initAdapter() {
LinearLayoutManager lm = new LinearLayoutManager(context);
recyclerView.setLoadingListener(this);
recyclerView.setLayoutManager(lm);
recyclerView.setArrowImageView(R.mipmap.pull_down_arrow);
recyclerView.setRefreshProgressStyle(ProgressStyle.BallPulseSync);
recyclerView.setLoadingMoreProgressStyle(ProgressStyle.BallPulseSync);
adapter = new NewsListAdapter();
recyclerView.setAdapter(adapter);
}
/**
* 首次加載數(shù)據(jù)
*/
private void firstLoadData() {
presenter.loadNewsList(FIRST_LOAD, pageNum);
}
/**
* 刷新數(shù)據(jù)
*/
private void refreshData() {
pageNum = 1;
presenter.loadNewsList(REFRESH, pageNum);
}
/**
* 加載更多
*/
private void loadMoreData() {
pageNum++;
presenter.loadNewsList(LOAD_MORE, pageNum);
}
/**
* 下拉刷新
*/
@Override
public void onRefresh() {
refreshData();
}
/**
* 加載更多
*/
@Override
public void onLoadMore() {
loadMoreData();
}
/**
* 加載成功回調(diào)
*
* @param data 列表數(shù)據(jù)
* @param loadType 加載類型
*/
@Override
public void loadSuccess(List<NewsEntity> data, int loadType) {
switch (loadType) {
case FIRST_LOAD:
adapter.refreshData(data);
recyclerView.refreshComplete();
break;
case REFRESH:
adapter.refreshData(data);
recyclerView.refreshComplete();
break;
case LOAD_MORE:
adapter.loadMoreData(data);
recyclerView.loadMoreComplete();
break;
}
}
/**
* 加載出錯
*
* @param msg 錯誤信息
*/
@Override
public void showError(String msg) {
ToastUtils.show(context, msg);
if (pageNum > 1) {
pageNum--;
}
recyclerView.refreshComplete();
recyclerView.loadMoreComplete();
}
/**
* 展示加載進度
*
* @param msg 加載信息
*/
@Override
public void showLoading(String msg) {
PromptDialog.getInstance().show(context,msg);
}
/**
* 關(guān)閉加載進度
*/
@Override
public void hideLoading() {
PromptDialog.getInstance().close();
}
}
在mvp的activity中我們通過presenter的實例來調(diào)用刷新或加載更多并且處理相應的view顯示 (PromptDialog 為Utils中封裝的一個簡單系統(tǒng)加載框)
mvvm中的viewModel 代碼
/**
* Created by mj
* on 2017/5/22.
* viewModel
*/
public class NewsListVm implements INewsListModel.LoadResponse {
/**
* 首次加載
*/
private final int FIRST_LOAD = 0;
/**
* 下拉刷新
*/
private final int REFRESH = 1;
/**
* 加載更多
*/
private final int LOAD_MORE = 2;
/**
* binding
*/
private ActivityMvVmBinding binding;
/**
* adapter
*/
private NewsListAdapter adapter;
/**
* 加載列表數(shù)據(jù)業(yè)務邏輯
*/
private NewsListModelBiz newsListModelBiz;
/**
* 頁數(shù)
*/
private int pageNum = 1;
/**
* context
*/
private Context context;
public NewsListVm(Context context, ActivityMvVmBinding binding, NewsListAdapter adapter) {
this.context = context;
this.binding = binding;
this.adapter = adapter;
init();
}
/**
* 初始化
*/
private void init() {
newsListModelBiz = new NewsListModelBiz();
firstLoadData();
}
/**
* 首次加載
*/
private void firstLoadData() {
PromptDialog.getInstance().show(context, "加載中...");
newsListModelBiz.load(FIRST_LOAD, pageNum, this);
}
/**
* 刷新數(shù)據(jù)
*/
public void setRefreshData() {
pageNum = 1;
newsListModelBiz.load(REFRESH, pageNum, this);
}
/**
* 加載更多
*/
public void loadMoreData() {
pageNum++;
newsListModelBiz.load(LOAD_MORE, pageNum, this);
}
@Override
public void loadSuccess(List<NewsEntity> data, int loadType) {
switch (loadType) {
case FIRST_LOAD:
adapter.refreshData(data);
binding.activityMvVmList.refreshComplete();
break;
case REFRESH:
adapter.refreshData(data);
binding.activityMvVmList.refreshComplete();
break;
case LOAD_MORE:
adapter.loadMoreData(data);
binding.activityMvVmList.loadMoreComplete();
break;
}
PromptDialog.getInstance().close();
}
@Override
public void loadFailure(String msg) {
// 加載失敗后的提示
if (pageNum > 1) {
pageNum--;
}
PromptDialog.getInstance().close();
binding.activityMvVmList.refreshComplete();
binding.activityMvVmList.loadMoreComplete();
}
}
viewModel中的代碼也比較簡單 主要是調(diào)用model層 加載數(shù)據(jù) 以及對model層返回的結(jié)果進行顯示险胰。
mvp中的presenter 代碼
/**
* Created by mj
* on 2017/5/22.
*/
public class NewsListPresenterImpl extends BasePresenter<INewsListView> implements INewsListPresenterBiz, INewsListModelBiz.LoadResponse {
/**
* 加載列表數(shù)據(jù)的業(yè)務邏輯處理
*/
private INewsListModelBiz iNewsListModelBiz;
public NewsListPresenterImpl() {
iNewsListModelBiz = new NewsListModelImpl();
}
@Override
public void loadNewsList(int loadType,int pageNum) {
if (mView != null) {
// 首次進入界面展示加載對話框
if (loadType == MvpActivity.FIRST_LOAD) {
mView.showLoading("加載中...");
}
iNewsListModelBiz.load(loadType,pageNum, this);
}
}
@Override
public void loadSuccess(List<NewsEntity> data, int loadType) {
if (mView != null) {
mView.loadSuccess(data, loadType);
mView.hideLoading();
}
}
@Override
public void loadFailure(String msg) {
if (mView != null) {
mView.showError(msg);
mView.hideLoading();
}
}
}
與mvvm中的viewModel類似 都是調(diào)用 model層 以及對model層返回的結(jié)果做UI顯示。omg~ 感覺這樣粘上來的代碼使文章變得很長~~~ 我盡量簡化一下矿筝,大家可以下載demo具體的看一下實現(xiàn)過程起便。
model層我就不再粘貼代碼了~ mvp中的model 層 與 mvvm中的model層是一樣的,都是做數(shù)據(jù)的準備 窖维,以及通過回調(diào)形式將數(shù)據(jù)返回榆综。接下來我們看一下mvvm中 的adapter 的實現(xiàn)方式
Mvvm Adapter中的代碼
/**
* Created by mj
* on 2017/5/22.
*/
public class NewsListAdapter extends BaseAdapter<NewsEntity, BindingVH> {
/**
* 沒有圖片的item 類型
*/
private final int NO_PIC = 0;
/**
* 有一張圖片的item 類型
*/
private final int ONE_PIC = 1;
/**
* 更多圖片的item 類型
*/
private final int MORE_PIC = 2;
/**
* 根據(jù)圖片數(shù)量判斷item 的類型
*
* @param position position
* @return itemType
*/
@Override
public int getItemViewType(int position) {
if (data.get(position).getPicNum() == 0) {
return NO_PIC;
} else if (data.get(position).getPicNum() == 1) {
return ONE_PIC;
} else {
return MORE_PIC;
}
}
@Override
public BindingVH createVH(ViewGroup parent, int viewType) {
ViewDataBinding viewDataBinding = null;
switch (viewType) {
case NO_PIC:
viewDataBinding = DataBindingUtil.inflate(inflater, R.layout.mv_vm_item_text, parent, false);
break;
case ONE_PIC:
viewDataBinding = DataBindingUtil.inflate(inflater, R.layout.mv_vm_item_one_pic, parent, false);
break;
case MORE_PIC:
viewDataBinding = DataBindingUtil.inflate(inflater, R.layout.mv_vm_item_more_pic, parent, false);
break;
}
return new BindingVH<>(viewDataBinding);
}
@Override
public void bindVH(BindingVH bindingVH, int position) {
bindingVH.getBinding().setVariable(BR.newsEntity, data.get(position));
bindingVH.getBinding().setVariable(BR.handle, this);
bindingVH.getBinding().setVariable(BR.position, position);
bindingVH.getBinding().executePendingBindings();
}
/**
* 點贊
*
* @param newsEntity entity
*/
public void thumbUpClick(NewsEntity newsEntity, int position) {
if (newsEntity.isNice()) {
newsEntity.setNice(false);
newsEntity.setNiceCount(newsEntity.getNiceCount() - 1);
ToastUtils.show(context, "取消點贊 position=" + position);
} else {
newsEntity.setNice(true);
newsEntity.setNiceCount(newsEntity.getNiceCount() + 1);
ToastUtils.show(context, "點贊成功 position=" + position);
}
}
}
實現(xiàn)一個3種item形式的adapter,僅僅用了不到100行的代碼(其中還包括空行和注釋) 主要歸功于 databinding 為我們承受了成噸的傷害~ 。
createVH(ViewGroup parent, int viewType)方法與
bindVH(BindingVH bindingVH, int position)方法都是BaseAdapter中的抽象類 和recyclerview.Adapter中的onCreateViewHolder()與onBindViewHolder()是一樣的陈辱。
我們先來看一下 createVH方法中 返回了一個BindingVH的對象奖年,并且根據(jù)不同的viewType 返回了不同的ViewDataBinding,那么 這個BindingVH 一定是ViewDataBinding的子類啦 ~ 沛贪,繼續(xù)上代碼~
/**
* Created by mj
* on 2017/5/22.
* binding view holder
*/
public class BindingVH<B extends ViewDataBinding> extends RecyclerView.ViewHolder {
/**
* viewDataBinding
*/
private B mBinding;
/**
* constructor
*
* @param binding viewDataBinding
*/
public BindingVH(B binding) {
super(binding.getRoot());
mBinding = binding;
}
/**
* @return viewDataBinding
*/
public B getBinding() {
return mBinding;
}
}
BindingVH繼承自RecyclerView.ViewHolder 這個類陋守,并且提供了獲取binding的方法 。
我們繼續(xù)回到createVH這個方法中利赋。在這里 我們設置布局的方式是通過 DatabindingUtil.inflate(...) 方法獲取的ViewDataBinding水评,我們在看一下 多圖布局的xml布局(另外兩個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.mj.design_patterns.R"/>
<import type="android.view.View"/>
<variable
name="newsEntity"
type="com.mj.design_patterns.mv_vm.bean.NewsEntity"/>
<variable
name="handle"
type="com.mj.design_patterns.mv_vm.adapter.NewsListAdapter"/>
<variable
name="position"
type="int"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:layout_marginTop="10dip"
android:ellipsize="end"
android:maxLines="1"
android:text="@{newsEntity.content}"
android:textColor="@color/c3"
android:textSize="15sp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dip"
android:layout_margin="10dip">
<ImageView
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_marginRight="5dip"
android:layout_weight="1"
android:scaleType="centerCrop"
app:imageUrl="@{newsEntity.imageUrls[0]}"/>
<ImageView
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:scaleType="centerCrop"
app:imageUrl="@{newsEntity.imageUrls[1]}"/>
<ImageView
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_marginLeft="5dip"
android:layout_weight="1"
android:scaleType="centerCrop"
android:visibility="@{newsEntity.getPicNum == 2 ? View.GONE : View.VISIBLE}"
app:imageUrl="@{newsEntity.imageUrls[2]}"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dip"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@{newsEntity.getDateSplicingPageNum}"
android:textColor="@color/c6"
android:textSize="12sp"/>
<!--android:src="@{newsEntity.isNice ? @drawable/nice_press :@drawable/nice_normal}"-->
<ImageView
android:layout_width="15dip"
android:layout_height="15dip"
android:onClick="@{()->handle.thumbUpClick(newsEntity,position)}"
app:resId="@{newsEntity.isNice ? R.mipmap.nice_press : R.mipmap.nice_normal}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:text="@{String.valueOf(newsEntity.niceCount)}"
android:textColor="@{newsEntity.isNice ? @color/appColor : @color/c6}"
android:textSize="12sp"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/listBgC"/>
</LinearLayout>
</layout>
在這個xml布局中我們在結(jié)合adapter類一起來說一下demo中的數(shù)據(jù)以及事件是如何綁定到xml中的。其實在adapter中每一個item都相當于一個ViewModel
我們就從布局中的信息從上往下說吧~ xml中的格式我前面已經(jīng)說過了 媚送,就不在贅述了中燥。
在data標簽中 我們看到了import 沒錯在這里我們可以引入包~ (在使用databinding時除了java.lang的包不需要import 其他xml中用到的都需要import進來)
variable 標簽中 name屬性的值是你隨意定義的(前提是你要知道定義這個名字的意義) type屬性就是我們需要導入的包了~ 比如我們用到了 NewsEntity這個實體類 我們就需要導入這個包,可以這個實體是怎么設置進來的呢塘偎? 我們來看adapter中的bindVH方法
我們通過viewDataBinding 把數(shù)據(jù)或事件傳遞進去疗涉。因為我們沒有 獲取具體的binding類型拿霉,所以我們通過調(diào)用setVariable(a,b)來設置。
a代表:通過BR類來查找xml中variable標簽中屬性name定義的名字
b代表:事件或數(shù)據(jù)
否則可以通過具體的binding類型實例來設置咱扣,比如
最后通過viewDatabinding.executePendingBindings(); 來實現(xiàn)綁定绽淘。
看上邊xml布局 文本的綁定方式為:
所有xml中無論是事件綁定還是數(shù)據(jù)綁定都要這種格式@{...}
因為我們已經(jīng)設置了newsEntity 所以在我畫圈的地方可以直接使用
圖片的綁定:
可是我們 實體里邊肯定是存的一個圖片地址啊~~ 這怎么實現(xiàn)綁定呢! 沒關(guān)系 databinding已經(jīng)給我們提供了方法:通過添加@BindingAdapter注解 databinding 就會為我們生成一個 attr對應的屬性
/**
* Created by mj
* on 2017/5/22.
*/
public class BindImageAdapter {
/**
* mv_vm xml 傳入url 加載圖片
* imageUrl 為xml中 的命名
*
* @param iv imageView
* @param path 圖片路徑
*/
@BindingAdapter({"imageUrl"})
public static void loadImage(ImageView iv, String path) {
Glide.with(iv.getContext()).load(path).into(iv);
}
/**
* mv_vm xml 設置 mipmap Resource
*
* @param iv imageView
* @param resId resource id
*/
@BindingAdapter({"resId"})
public static void loadMipmapResource(ImageView iv, int resId) {
iv.setImageResource(resId);
}
}
我們將圖片地址傳進來 我們在用圖片加載框架對其進行加載闹伪。
@BindingAdapter({"imageUrl"}) imageUrl就是 對應的xml中 自定義的屬性名字沪铭。這樣就實現(xiàn)了圖片的綁定。我們實體類中是 imageUrls 是一個string數(shù)組類型 所以 我們在xml中就直接可以使用下標的方式獲取圖片的url(順便提一下 偏瓤,在xml實現(xiàn)綁定時 我們根本不用擔心數(shù)組越界 對象為空這些~~ 因為 databinding已經(jīng)為我們做好了處理)
接下來我們看一下點贊的事件綁定:
喔~ 這里支持lambda 寫法 前面已經(jīng)寫過了 在bindVH方法中設置handle 所以我們這里可以直接用杀怠。看handle 所指向的方法在adapter中的實現(xiàn)
在xml中的onClick方法直接指向了adapter的thumbUpClick方法~ 厅克,以及參數(shù)也是在xml中傳遞過來的~~ 那么看看我們的點贊 到底是如何實現(xiàn)雙向綁定的呢~~ 相信很多同學剛才已經(jīng)看到了 上圖中ImageView中的resId屬性了~ 赔退。里邊寫了一個三元表達式,也就是說 如果 newEntity中的 isNice為true 就顯示 聚焦狀態(tài)的點贊圖片已骇,否則顯示正常的點贊圖片
在newsEntity 類里繼承了 dataBinding 的 BaseObservable离钝,并且看下圖:
isNice方法添加了Bindable 注釋 并且 setNice方法里邊調(diào)用了notifyChange()方法 票编。這樣就實現(xiàn)了雙向綁定 數(shù)據(jù)改變UI改變 UI改變數(shù)據(jù)也隨之改變的邏輯~~ 褪储。(還有其他的方式 我就不贅述了,請看google文檔慧域,前面已經(jīng)給了傳送門)鲤竹。
吐槽一下 databinding 居然不支持在xml中設置@mipmap,只支持@drawable昔榴。 所以有心的同學在上面已經(jīng)看到了 我們是通過@BindingAdapter方式來實現(xiàn)的(可以上滑看一下)
看上面的gif圖 日期后邊我拼接了一個當前頁數(shù)辛藻。這里在啰嗦一下 在xml中使用字符串的拼接~ (xml不允許寫入中文,否則報錯)
我們可以這樣實現(xiàn)拼接
android:text="@{@string/listPageNumDisplay(newsEntity.dateStr,newsEntity.pageNum)}"
在strings.xml中定義替換
<string name="listPageNumDisplay">%1$s--##第%2$d頁數(shù)據(jù)</string>
但是在我們的項目中 沒有使用這種方式
而是直接在實體類里邊(NewsEntity)單提出一個方法在xml方便調(diào)用
/**
* 獲取日期拼接頁數(shù)的字符串
* @return 格式--> 2017年5月23日--##第1頁數(shù)據(jù)
*/
public String getDateSplicingPageNum(){
return dateStr+"--##第"+pageNum+"頁數(shù)據(jù)";
}
實現(xiàn)方式有很多種~ 大家可以動手去嘗試一下
**mvp中的adapter實現(xiàn)我就不再粘貼了~ 建議還是直接下載代碼來看互订,比較直觀方便(有什么問題可以給我留言)我就不再多啰嗦了 ~~~ **
<a >
github demo下載地址
</a>
<a >csdn demo下載地址</a>