本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出
概述
在前文快速開發偷懶必備(一)中,我們利用Adapter模式封裝了一個庫,能快速為任意ViewGroup新增子View。
有如下特點:
- 快速簡單使用
- 支援任意ViewGroup
- 無耦合
- 無侵入性
- Item支援多種型別
在庫中V1.1.0版本,我也順手加入了RecyclerView、ListView、GridView的通用Adapter功能,庫地址在這裡。
現在V1.2.0版本釋出,我又加入了我最近超愛的一個技術,DataBinding。
封裝了一套一行程式碼實現花式列表的Adapter。
即利用DataBinding實現RecyclerView中快速使用的Adapter。
以後不管寫多種type還是單type的列表,利用DataBinding 和本庫,都只需要一行程式碼!
這裡也算是安利DataBinding吧,真的超好用。還沒使用的朋友們,在看到本文可以如此簡單寫花式列表後,建議去學習一下。
先看用法吧,簡單粗暴到沒朋友。
轉載請標明出處:
gold.xitu.io/post/584fbd…
本文出自:【張旭童的稀土掘金】(gold.xitu.io/user/56de21…)
程式碼傳送門:喜歡的話,隨手點個star。多謝
github.com/mcxtzhang/a…
用法
使用必讀:
BaseBindingAdapter
利用DataBinding提供的動態繫結技術,使用BR.data
封裝資料、BR.itemP
封裝點選事件。所以對layout
有以下要求:
- layout中 資料name起名data
- layout中 點選事件Presenter起名 itemP
如:
<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">
<data>
<variable
name="itemP"
type="mcxtzhang.commonviewgroupadapter.databinding.rv.single.DBSingleActivity.SingleItemPresenter"/>
<variable
name="data"
type="mcxtzhang.commonviewgroupadapter.databinding.rv.single.DBSingleBean"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="1dp"
android:background="@color/colorAccent"
android:onClick="@{v->itemP.onItemClick(data)}"
android:orientation="horizontal">
<ImageView
android:id="@+id/ivAvatar"
android:layout_width="200dp"
android:layout_height="200dp"
app:netUrl="@{data.avatar}"
tools:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{data.name}"
tools:text="測試多種"/>
</LinearLayout>
</layout>複製程式碼
1 單Item列表
效果如圖:
順帶演示了BaseBindingAdapter封裝的一些增刪功能。
用法:
和其他BaseAdapter用法一致:
- 建構函式只需要傳入context,datas,layout
mAdapter = new BaseBindingAdapter(this, mDatas, R.layout.item_db_single);複製程式碼
好了,列表已經出來了。我不騙你,就這一句話。
如果需要設定點選事件(點選事件設定所有型別都一樣,下不贅述):
//★ 設定Item點選事件
mAdapter.setItemPresenter(new SingleItemPresenter());複製程式碼
/**
* ★ Item點選事件P
*/
public class SingleItemPresenter {
public void onItemClick(DBSingleBean data) {
data.setName("修改之後立刻見效");
}
}複製程式碼
特殊需求:
如果有特殊需求,可傳入兩個泛型,重寫onBindViewHolder搞事情:
// ★泛型D:是Bean型別,如果有就傳。 泛型B:是對應的xml Layout的Binding類
mAdapter = new BaseBindingAdapter<DBSingleBean, ItemDbSingleBinding>(this, mDatas, R.layout.item_db_single) {
@Override
public void onBindViewHolder(BaseBindingVH<ItemDbSingleBinding> holder, int position) {
//★super一定不要刪除
super.onBindViewHolder(holder, position);
//如果有特殊需求,可傳入兩個泛型,重寫onBindViewHolder搞事情。
ItemDbSingleBinding binding = holder.getBinding();
DBSingleBean data = mDatas.get(position);
}
};複製程式碼
2 多Item同種資料型別列表
一般是像IM那種列表,雖然Item不同,但是資料結構是同一個。用法,一句話~
效果如圖:
用法:
- 資料結構(JavaBean)需實現
IBaseMulInterface
介面,根據情況返回不同的layout。 - 建構函式只需要傳入context,datas.
mAdapter = new BaseMulTypeBindingAdapter(this, mDatas);複製程式碼
複雜列表依然一句話。
public class MulTypeSingleBean extends BaseObservable implements IBaseMulInterface {
private String avatar;
private String name;
private boolean receive;
@Override
public int getItemLayoutId() {
if (isReceive()) {
return R.layout.item_db_mul_1;
} else {
return R.layout.item_db_mul_2;
}
}
}複製程式碼
特殊需求:
如果有特殊需求,可傳入資料結構的泛型,避免強轉,重寫onBindViewHolder()
方法,但是Binding類 不可避免的需要強轉了:
mAdapter = new BaseMulTypeBindingAdapter<MulTypeSingleBean>(this, mDatas) {
@Override
public void onBindViewHolder(BaseBindingVH<ViewDataBinding> holder, int position) {
super.onBindViewHolder(holder, position);
//如果有特殊需求,可傳入資料結構的泛型,避免強轉
MulTypeSingleBean data = mDatas.get(position);
//Binding類 不可避免的需要強轉了
ViewDataBinding binding = holder.getBinding();
switch (data.getItemLayoutId()) {
case R.layout.item_db_mul_1:
ItemDbMul1Binding itemDbMul1Binding = (ItemDbMul1Binding) binding;
break;
case R.layout.item_db_mul_2:
ItemDbMul2Binding itemDbMul2Binding = (ItemDbMul2Binding) binding;
break;
}
}
};複製程式碼
3 多Item、多種資料型別列表
各大APP首頁,Banner、列表、推薦混排,資料結構肯定不同,但是依然只要一句程式碼搞定Adapter!
效果如圖:
用法:
- 資料結構(JavaBean)需分別實現
IBaseMulInterface
介面,返回資料結構對應的layout。 - 建構函式只需要傳入context,datas.
mAdapter = new BaseMulTypeBindingAdapter(this, mDatas);複製程式碼
public class MulTypeMulBean1 extends BaseObservable implements IBaseMulInterface {
private String avatar;
private String name;
@Override
public int getItemLayoutId() {
return R.layout.item_db_mulbean_1;
}
}複製程式碼
public class MulTypeMulBean2 extends BaseObservable implements IBaseMulInterface {
private String background;
@Override
public int getItemLayoutId() {
return R.layout.item_db_mulbean_2;
}
}複製程式碼
特殊需求:
如果有特殊需求,重寫onBindViewHolder()
方法,但是資料結構 和 Binding類 都不可避免的需要強轉了:
mAdapter = new BaseMulTypeBindingAdapter(this, mDatas) {
@Override
public void onBindViewHolder(BaseBindingVH holder, int position) {
super.onBindViewHolder(holder, position);
//如果有特殊需求 重寫onBindViewHolder方法
// 資料結構 和 Binding類 都不可避免的需要強轉了
ViewDataBinding binding = holder.getBinding();
switch (getItemViewType(position)) {
case R.layout.item_db_mul_1:
ItemDbMul1Binding itemDbMul1Binding = (ItemDbMul1Binding) binding;
MulTypeMulBean1 data1 = (MulTypeMulBean1) mDatas.get(position);
break;
case R.layout.item_db_mul_2:
ItemDbMul2Binding itemDbMul2Binding = (ItemDbMul2Binding) binding;
MulTypeMulBean2 data2 = (MulTypeMulBean2) mDatas.get(position);
break;
}
}
};複製程式碼
4 不能忘了上文的ViewGroup呀
對上文封裝的ViewGroup型別Adapter也提供DataBinding的支援。
效果如圖:
當然還是流式佈局搭配史上整合最叼側滑選單控制元件。
用法:
和上文快速開發偷懶必備(一)一樣,只是Adapter換成SingleBindingAdapter
mAdapter = new SingleBindingAdapter<>(this, mDatas = iniDatas(), R.layout.item_db_flow_swipe);複製程式碼
如果需要設定點選事件:
mAdapter.setItemPresenter(new ItemDelPresenter());複製程式碼
設計思路與實現
使用起來如此爽快,其實寫起來也很簡單。
注意類BaseBindingAdapter
和BaseMulTypeBindingAdapter
都不是abstract
的,這說明我們不需要重寫任何方法。
利用DataBinding,我們在BasexxxAdapter內部和xml分別做View的建立和資料繫結的工作。
UML類圖
先簡要概括
BaseBindingVH
繼承自RecyclerView.ViewHolder
,持有T extends ViewDataBinding
型別的mBinding
變數。利用ViewDataBinding
我們將不用再寫任何ViewHolder
。BaseBindingAdapter
,繼承自RecyclerView.Adapter
,依賴BaseBindingVH
,onCreateViewHolder(ViewGroup parent, int viewType)
方法返回BaseBindingVH
作為ViewHolder
。
內部持有三個重要變數:資料對應layout,資料集,Item點選事件處理類。資料對應layout會在onCreateViewHolder(ViewGroup parent, int viewType)
用到。剩下兩個變數在onBindViewHolder()
用到。對外暴漏setItemPresenter(Object itemPresenter)
供設定點選事件處理類。IBaseMulInterface
介面和快速開發偷懶必備(一)提到的一樣,返回某個資料結構對應的layout,除此之外,本文還有一個十分tricky之處,利用返回的R.layout.itemxxxx
作為ItemViewType
,在BaseMulTypeBindingAdapter
會用到。BaseMulTypeBindingAdapter
繼承自BaseBindingAdapter
,但是它不再關心mLayoutId
變數,它利用IBaseMulInterface
介面返回的R.layout.itemxxxx
作為ItemViewType
,這樣在onCreateViewHolder(ViewGroup parent, int viewType)
的時候,就可以直接用viewType
構造出ItemView
。不再依賴mLayoutId
變數。這是一個我很得意的設計,我在優雅為RecyclerView增加HeaderView一文中,也曾用過這個方法。
BaseBindingVH
BaseBindingVH
算是一個核心類,但是又十分簡單。它繼承自RecyclerView.ViewHolder
,持有由泛型傳入的T extends ViewDataBinding
型別的mBinding
變數。
唯一建構函式,需要一個T t
變數,然後呼叫super()
傳入t.getRoot()
完成itemView
的賦值。同時對mBinding
變數賦值。
對外暴漏getBinding()
返回mBinding
變數。
利用ViewDataBinding
我們將不用再寫任何ViewHolder
。
public class BaseBindingVH<T extends ViewDataBinding> extends RecyclerView.ViewHolder {
protected final T mBinding;
public BaseBindingVH(T t) {
super(t.getRoot());
mBinding = t;
}
public T getBinding() {
return mBinding;
}
}複製程式碼
BaseBindingAdapter
BaseBindingAdapter
,繼承自RecyclerView.Adapter
,依賴BaseBindingVH
,將BaseBindingVH
作為泛型傳給RecyclerView.Adapter
。
同時BaseBindingAdapter
本身接受兩個泛型,<D, B extends ViewDataBinding>
。
- 泛型沒有特殊需求可以不傳
- 泛型D:是Bean型別,如果有就傳。
- 泛型B:是對應的xml Layout的Binding類
傳入不傳入泛型的區別已經在第二節具體用法裡進行了演示,不再贅述。
內部持有三個重要變數:
- 資料對應layout
int mLayoutId;
- 資料集
List<D> mDatas;
- Item點選事件處理類。
Object ItemPresenter;
mLayoutId
和mDatas
都由建構函式傳入,沒啥好說的。
對外暴漏setItemPresenter(Object itemPresenter)
供設定點選事件處理類ItemPresenter
。ItemPresenter
是Object
型別,這樣才不care你set的Item點選事件處理類是什麼鬼。
onCreateViewHolder(ViewGroup parent, int viewType)
方法返回BaseBindingVH
作為ViewHolder
。mLayoutId
會在onCreateViewHolder(ViewGroup parent, int viewType)
用到,再根據泛型B強轉成對應的ViewDataBinding
:
BaseBindingVH<B> holder = new BaseBindingVH<B>((B) DataBindingUtil.inflate(mInfalter, mLayoutId, parent, false));複製程式碼
會在onBindViewHolder()
方法裡,利用DataBinding動態繫結ViewDataBinding.setVariable(BR.itemP, ItemPresenter);
為每個Item設定點選事件。
同時,資料也是同樣在裡面繫結的:setVariable(BR.data, mDatas.get(position))
。
重點程式碼如下:
public class BaseBindingAdapter<D, B extends ViewDataBinding> extends RecyclerView.Adapter<BaseBindingVH<B>> {
protected Context mContext;
protected int mLayoutId;
protected List<D> mDatas;
protected LayoutInflater mInfalter;
//用於設定Item的事件Presenter
protected Object ItemPresenter;
public BaseBindingAdapter(Context mContext, List mDatas, int mLayoutId) {
this.mContext = mContext;
this.mLayoutId = mLayoutId;
this.mDatas = mDatas;
this.mInfalter = LayoutInflater.from(mContext);
}
@Override
public BaseBindingVH<B> onCreateViewHolder(ViewGroup parent, int viewType) {
BaseBindingVH<B> holder = new BaseBindingVH<B>((B) DataBindingUtil.inflate(mInfalter, mLayoutId, parent, false));
onCreateViewHolder(holder);
return holder;
}
/**
* 如果需要給Vh設定監聽器啥的 可以在這裡
*
* @param holder
*/
public void onCreateViewHolder(BaseBindingVH<B> holder) {
}
/**
* 子類除了繫結資料,還要設定監聽器等其他操作。
* 可以重寫這個方法,不要刪掉super.onBindViewHolder(holder, position);
*
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(BaseBindingVH<B> holder, int position) {
holder.getBinding().setVariable(BR.data, mDatas.get(position));
holder.getBinding().setVariable(BR.itemP, ItemPresenter);
holder.getBinding().executePendingBindings();
}
/**
* 用於設定Item的事件Presenter
*
* @param itemPresenter
* @return
*/
public BaseBindingAdapter setItemPresenter(Object itemPresenter) {
ItemPresenter = itemPresenter;
return this;
}
}複製程式碼
BaseBindingAdapter
內部也封裝瞭如下方法,方便資料重新整理,增刪(定向重新整理)呼叫:
/**
* 重新整理資料,初始化資料
*
* @param list
*/
public void setDatas(List<D> list) {
if (this.mDatas != null) {
if (null != list) {
List<D> temp = new ArrayList<D>();
temp.addAll(list);
this.mDatas.clear();
this.mDatas.addAll(temp);
} else {
this.mDatas.clear();
}
} else {
this.mDatas = list;
}
notifyDataSetChanged();
}
/**
* 刪除一條資料
* 會自動定向重新整理
*
* @param i
*/
public void remove(int i) {
if (null != mDatas && mDatas.size() > i && i > -1) {
mDatas.remove(i);
notifyItemRemoved(i);
}
}
/**
* 新增一條資料 至隊尾
* 會自動定向重新整理
*
* @param data
*/
public void add(D data) {
if (data != null && mDatas != null) {
mDatas.add(data);
notifyItemInserted(mDatas.size());
}
}
/**
* 在指定位置新增一條資料
* 會自動定向重新整理
* <p>
* 如果指定位置越界,則新增在隊尾
*
* @param position
* @param data
*/
public void add(int position, D data) {
if (data != null && mDatas != null) {
if (mDatas.size() > position && position > -1) {
mDatas.add(position, data);
notifyItemInserted(position);
} else {
add(data);
}
}
}
/**
* 載入更多資料
*
* @param list
*/
public void addDatas(List<D> list) {
if (null != list) {
List<D> temp = new ArrayList<D>();
temp.addAll(list);
if (this.mDatas != null) {
this.mDatas.addAll(temp);
} else {
this.mDatas = temp;
}
notifyDataSetChanged();
}
}複製程式碼
IBaseMulInterface介面
來點簡單的.
IBaseMulInterface
介面和快速開發偷懶必備(一)提到的一樣,返回某個資料結構對應的layout.
除此之外,本文還有一個十分tricky之處,利用返回的R.layout.itemxxxx
作為ItemViewType
,在BaseMulTypeBindingAdapter
會用到。
因為不同的R.layout.itemxxxx
對於RecyclerView來說一定是不同的Item,
BaseMulTypeBindingAdapter
多種ItemType的Base類
BaseMulTypeBindingAdapter
繼承自BaseBindingAdapter
,但是它不再關心mLayoutId
變數。因此它傳給父類的泛型B就是ViewDataBinding
類本身。解釋如下:
基類的泛型B:不用傳,因為多種ItemType 肯定Layout長得不一樣,那麼Binding類也不一樣,傳入沒有任何意義
泛型T:多Item多Bean情況可以不傳。如果只有一種Bean型別,可以傳入Bean,實現IBaseMulInterface介面。
或者傳入IBaseMulInterface介面,可以拿到 getItemLayoutId(),
但是通過getItemViewType(int position),一樣。所以多Item多Bean建議不傳。
傳入不傳入泛型的區別已經在第二節具體用法裡進行了演示,不再贅述。
getItemViewType()
直接返回 IBaseMulInterface
介面的返回值。
在onCreateViewHolder(ViewGroup parent, int viewType)
的時候,直接用viewType
構建ViewDataBinding
(ItemView
)。不再依賴mLayoutId
變數。
這是一個我很得意的設計,我在優雅為RecyclerView增加HeaderView一文中,也曾用過這個方法新增頭部。
完整程式碼如下:
public class BaseMulTypeBindingAdapter<T extends IBaseMulInterface> extends BaseBindingAdapter<T, ViewDataBinding> {
public BaseMulTypeBindingAdapter(Context mContext, List<T> mDatas) {
super(mContext, mDatas);
}
@Override
public int getItemViewType(int position) {
return mDatas.get(position).getItemLayoutId();
}
@Override
public BaseBindingVH<ViewDataBinding> onCreateViewHolder(ViewGroup parent, int viewType) {
BaseBindingVH<ViewDataBinding> holder = new BaseBindingVH<ViewDataBinding>(DataBindingUtil.inflate(mInfalter, viewType, parent, false));
onCreateViewHolder(holder);
return holder;
}
}複製程式碼
ViewGroup Adapter的實現
單item
繼承SingleAdapter
,增加ItemPresenter
,在getView()
完成View建立和繫結。
public class SingleBindingAdapter<D, B extends ViewDataBinding> extends SingleAdapter<D> {
//用於設定Item的事件Presenter
protected Object ItemPresenter;
/**
* 用於設定Item的事件Presenter
*
* @param itemPresenter
* @return
*/
public SingleBindingAdapter setItemPresenter(Object itemPresenter) {
ItemPresenter = itemPresenter;
return this;
}
public SingleBindingAdapter(Context context, List<D> datas, int itemLayoutId) {
super(context, datas, itemLayoutId);
}
//重寫利用DataBinding做
@Override
public View getView(ViewGroup parent, int pos, D data) {
ViewDataBinding binding = DataBindingUtil.inflate(mInflater, mItemLayoutId, parent, false);
View itemView = binding.getRoot();
onBindView(parent, itemView, data, pos);
binding.setVariable(BR.data, data);
binding.setVariable(BR.itemP, ItemPresenter);
return itemView;
}
//空實現即可,因為DataBinding的實現都是在xml裡做
@Override
public void onBindView(ViewGroup parent, View itemView, D data, int pos) {
}
}複製程式碼
多Item:
更簡單了,繼承SingleBindingAdapter
。重寫getView()
即可。
public class MulTypeBindngAdapter<T extends IMulTypeHelper> extends SingleBindingAdapter<T, ViewDataBinding> {
public MulTypeBindngAdapter(Context context, List<T> datas) {
super(context, datas, -1);
}
//重寫利用DataBinding做
@Override
public View getView(ViewGroup parent, int pos, T data) {
ViewDataBinding binding = DataBindingUtil.inflate(mInflater, data.getItemLayoutId(), parent, false);
View itemView = binding.getRoot();
onBindView(parent, itemView, data, pos);
binding.setVariable(BR.data, data);
binding.setVariable(BR.itemP, ItemPresenter);
return itemView;
}
}複製程式碼
總結
程式碼傳送門:喜歡的話,隨手點個star。多謝
github.com/mcxtzhang/a…
本文利用DataBinding
的ViewDataBinding
直接略去寫ViewHolder
。
利用Object
型別的ItemPresenter
,相容解決了點選事件的設定。
最得意的設計,還是利用R.layout.xxxx
這些佈局檔案int型別的的RID,作為ItemViewType
,一箭雙鵰。
DataBinding
很強,希望大家快點擁抱它。
to do list
- ViewGroup Adapter 考慮加入複用快取池
- ViewGroup Adapter ,考慮替換
onBindView()
的ItemView
->通用的ViewHolder
,這樣可以少寫一些findViewById()
程式碼 整合DataBinding 的通用Adapter入庫。- 完善 RecyclerView、ListView的通用Adapter,支援多種ItemViewType。
- 加入一些自定義ViewGroup入庫,例如流式佈局,九宮格,Banner輪播圖。
轉載請標明出處:
gold.xitu.io/post/584fbd…
本文出自:【張旭童的稀土掘金】(gold.xitu.io/user/56de21…)
程式碼傳送門:喜歡的話,隨手點個star。多謝
github.com/mcxtzhang/a…