但使书种多,会有岁稔时。这篇文章主要讲述Android快速开发偷懒必备 支持DataBinding啦~爽炸,一行实现花式列表相关的知识,希望能为你提供帮助。
本篇文章已授权微信公众号 guolin_blog ( 郭霖) 独家发布概述 在前文快速开发偷懒必备( 一) 中, 我们利用Adapter模式封装了一个库, 能快速为任意ViewGroup添加子View。
转载请标明出处:
http://blog.csdn.net/zxt0601/article/details/53618694
本文出自:【张旭童的博客】(http://blog.csdn.net/zxt0601)
代码传送门: 喜欢的话, 随手点个star。多谢
https://github.com/mcxtzhang/all-base-adapter
有如下特点:
* 快速简单使用
* 支持任意ViewGroup
* 无耦合
* 无侵入性
* Item支持多种类型
在库中V1.1.0版本, 我也顺手加入了RecyclerView、ListView、GridView的通用Adapter功能, 库地址在这里。
现在V1.2.0版本发布, 我又加入了我最近超爱的一个技术, DataBinding。
封装了一套一行代码实现花式列表的Adapter。
即利用DataBinding实现RecyclerView中快速使用的Adapter。
以后不管写多种type还是单type的列表, 利用DataBinding 和本库, 都只需要一行代码!
这里也算是安利DataBinding吧, 真的超好用。还没使用的朋友们, 在看到本文可以如此简单写花式列表后, 建议去学习一下。
先看用法吧, 简单粗暴到没朋友。
用法 使用必读:
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("
修改之后立刻见效"
);
}
}
特殊需求: 【Android快速开发偷懒必备 支持DataBinding啦~爽炸,一行实现花式列表】如果有特殊需求, 可传入两个泛型, 重写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
算是一个核心类,
但是又十分简单。它继承自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。多谢
https://github.com/mcxtzhang/all-base-adapter
本文利用
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轮播图。
转载请标明出处:
http://blog.csdn.net/zxt0601/article/details/53618694
本文出自:【张旭童的博客】(http://blog.csdn.net/zxt0601)
代码传送门: 喜欢的话, 随手点个star。多谢
https://github.com/mcxtzhang/all-base-adapter
推荐阅读
- Android注解使用之通过annotationProcessor注解生成代码实现自己的ButterKnife框架
- Android评论图片可移动顺序选择器
- React Native Android 应用层实战沦陷记
- Android RecycleView + CardView 控件简析
- android之网络编程
- Android中AlarmManager使用示例(持续更新)
- Android 7.0 UICC 分析
- Android:OpenFire 相关API (持续更新)
- Android-Sqlite数据库的操作