接着上篇,这里想分享ListAdapter、RecyclerView的封装。众所周知,ListAdapter以及RecyclerView都是列表控件,Google更推荐大家使用RecyclerView。关于RecyclerView的基本使用,不在本篇的讨论范围内。
ListAdapter
ListAdapter作为ListView的数据适配器,为ListView提供数据源。在我们的频繁使用中,发现:对Adapter的编写主要集中在几部分:
- 实现
getView(int position, View convertView, ViewGroup parent)
方法 - 数据集合的增删修改,频繁手动调用
notifyDataSetChange()
方法
而在getView()
方法中,我们通常会做这么几件事:
- 设置布局文件
- 数据与控件的绑定
- 控件事件注册
转化成代码:
if (convertView == null) {
convertView = View.inflate(context, getLayoutId(), null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = convertView.getTag();
}
bindItem(); //绑定数据
setEvent(); //绑定事件
return convertView;
由于Adapter可多个itemType,因此封装的时候缤纷两路,RecyclerAdapter的封装也是如此。
Step1 封装XListAdapter
,作为所有Adapter的基类,子类可实现getView()
方法
XListAdapter
中主要实现了数据集的增删修改、常用操作、事件绑定接口 等功能。
+ addData()
+ setData()
+ clearData
+ addElement()
+ removeElement()
+ getColor()
+ getDrawable()
+ visible()
+ gone()
+ invisible()
...
Step2 封装SimpleListAdapter
,实现单viewType的需求
核心如下:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
H holder = null;
T item = data.get(position);
if (convertView == null) {
convertView = View.inflate(context, getLayoutId(), null);
holder = newViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (H) convertView.getTag();
}
convert(holder, item, position);
return convertView;
}
子类则摆脱了这段魔鬼一样的代码,只需要实现这三个方法:
protected abstract H newViewHolder(View convertView); //创建viewHolder
protected abstract int getLayoutId(); //设置布局资源id
protected abstract void convert(H holder, T item, int position); //数据绑定与事件绑定
Step3 事件绑定接口
为了方便事件的统一处理,我抽象了一个抽象类ListItemCallback
public void onItemClick(int position, T model, int tag) {} //单击
public void onItemLongClick(int position, T model, int tag) {} //长按
参数:
- position : 就是getView中的position,位置
- model : 绑定的数据实体
- tag : 事件标识,自己定义
a. 为什么定义的是抽象类,而不是接口?
因为接口中的方法需要全部实现,而抽象类可以选择性的override,显然,我们通常只需要重写
onItemClick
方法
b. 如何使用呢?
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (getCallback() != null) {
getCallback().onItemClick(position, item, TAG_VIEW);
}
}
});
c. 为什么我不选择很常见的SparseArray
那种封装的CommonAdapter
因为我不喜欢那种编码style,建议将Adapter单独类
RecyclerView
RecyclerView的封装一般体现在:
- 下拉刷新、上拉加载更多
- header、footer
- divider
- ...
鉴于此,我之前搞了个ARecyclerView,主要有这些特性:
- ARecyclerView继承自RecyclerView,它就是一个封装了常见功能的RecyclerView,而不是继承FrameLayout
- ARecyclerView中实现了Header、Footer,header和Footer可以有多个
- ARecyclerView的每一个header、footer的viewType是不同的,而大部分开源库的header、footer的viewtype是相同的,其直接后果是界面卡顿
- ARecyclerView可以做出几乎任何的界面效果,可以取代ScrollView,你只需要使用header或者footer
- ARecyclerView中实现了上拉加载更多,可以自定义加载更多的效果,只需要实现LoadMoreUIHandler接口即可
- ARecyclerView并未实现下拉刷新功能,您可以选择SwipeRefreshLayout或者其他的下拉刷新viewGroup包裹,即你可以自由选择下拉刷新功能的实现。
- 为了方便自定义使用,特别集成了XRecyclerContentLayout控件,你可以根据业务进行扩展,XRecyclerContentLayout只是一个示例,当然也可以满足绝大部分需求了
由于其实现相对复杂,推荐大家可以去看看源码,app mudule中是完整的实例
在XDroid
中,我新增了SimpleRecAdapter
,应对单itemType的需求。具体思想和ListAdapter很相似
public abstract class SimpleRecAdapter extends RecyclerAdapter {
public SimpleRecAdapter(Context context) {
super(context);
}
@Override
public F onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(getLayoutId(), parent, false);
return newViewHolder(view);
}
public abstract F newViewHolder(View itemView);
public abstract int getLayoutId();
}
T 是item的实体类,F是ViewHolder的实体类
下一篇会谈谈ContentLayout
的实现过程。
XDroid项目是我在两年的开发中积累的一个Android快速开发框架,目前包含UI层、缓存、图片加载、日志、路由、Api请求、事件订阅、工具类等。下一步会进行mvp、rx全家桶、retrofit、权限适配等工作。欢迎大家提出宝贵意见,指正不足。
最后附上链接: https://github.com/limedroid/XDroid