项目中,我们用得最多的元素就是列表了,在Android 中,实现列表用原生的RecyclerView就能满足需求,关于RecyclerView 的基础使用这里不做过多的介绍,网上有太多的博文介绍了。本篇文章将介绍自己封装的一个Adapter,帮你快速高效的添加一个列表(包括单 Item 列表和多item列表)。
理念
1, 构造一个通用的Adapter模版,避免每添加一个列表就要写一个Adapter,避免写Adapter中的大量重复代码。
2,通过组装的方式来构建Adapter,将每一种(ViewType不同的)Item抽象成一个单独组件,Adapter 就是一个壳,我们只需要向Adapter中添加Item就行,这样做的好处就是减少耦合,去掉一种item 或者添加一种item对于列表是没有任何影响的。
3,高内聚,低耦合,扩展方便。
思路
为每一种 viewType 定义一个Cell,Cell就是上面提到的独立组件,它负责创建ViewHolder,数据绑定和逻辑处理。它有2个重要的方法,onCreateViewHolder 负责创建ViewHolder,onBindViewHolder负责数据绑定,这两个方法的定义和生命周期同Adapter种的2个方法一样,事实上,Adapter 中的onCreateViewHolder和onBindViewHolder 最终调用的是Cell中的方法。
一种 ViewType 对应一个Cell
看一个示例:
![](https://i-blog.csdnimg.cn/blog_migrate/cf74616799c483091340d5bff54f3f8c.webp?x-image-process=image/format,png)
如上图:以豆瓣APP的首页为例,文章包含图片和视频的两个Item 的布局是不同的,因此,可以添加两个Cell(ImageCell和VideoCell)来分别处理这两种Item。
有了Cell之后,要向列表添加添加Header和Footer 的需求就很简单了,我们直接添加一个HeaderCell和FooterCell 就可以了,也不用更改Adapter代码,是不是很方便。此外,还可以用Cell实现列表LoadMore(加载更多)状态、Loadding(加载中)状态、Empty(空页面)状态、Error(出错)状态 View的显示。
包结构
![](https://i-blog.csdnimg.cn/blog_migrate/5edddc45bd1f4ce380189e6c149e6272.webp?x-image-process=image/format,png)
介绍:1,base:base包下面为Lib的主要代码,一个Cell接口和三个抽象类,分别抽取了Adapter,ViewHolder,Cell的公共逻辑。
2,cell:cell包下面有4个cell,分别显示列表的LoadMore,Loading,Empty,Error状态。
3,fragment:有一个Fragment抽象类,定义了一个UI模版(不需要额外添加布局文件),要实现列表的界面只需要继承AbsBaseFragment,实现几个方法添加数据就OK。
具体代码
1,Cell 接口定义
/**
* Created by zhouwei on 17/1/19.
*/
public interface Cell {
/**
* 回收资源
*
*/
public void releaseResource();
/**
* 获取viewType
* @return
*/
public int getItemType();
/**
* 创建ViewHolder
* @param parent
* @param viewType
* @return
*/
public RVBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType);
/**
* 数据绑定
* @param holder
* @param position
*/
public void onBindViewHolder(RVBaseViewHolder holder, int position);
}
定义了4个方法,除了回收资源的方法releaseResource(),其它三个和Adapter中的一样。
2,RVBaseCell
/**
* Created by zhouwei on 17/1/19.
*/
public abstract class RVBaseCell<T> implements Cell {
public RVBaseCell(T t){
mData = t;
}
public T mData;
@Override
public void releaseResource() {
// do nothing
// 如果有需要回收的资源,子类自己实现
}
}
抽象类,接受一个范型T(Cell接受的数据实体),实现了releaseResource方法,但什么事也没干,因为有很多简单的Cell没有资源回收,就不需要实现。如果子类Cell 有资源回收,重写这个方法就可以了。
3, RVBaseViewHolder
/**
* Created by zhouwei on 17/1/19.
*/
public class RVBaseViewHolder extends RecyclerView.ViewHolder{
private SparseArray<View> views;
private View mItemView;
public RVBaseViewHolder(View itemView) {
super(itemView);
views = new SparseArray<>();
mItemView = itemView;
}
/**
* 获取ItemView
* @return
*/
public View getItemView() {
return mItemView;
}
public View getView(int resId) {
return retrieveView(resId);
}
public TextView getTextView(int resId){
return retrieveView(resId);
}
public ImageView getImageView(int resId){
return retrieveView(resId);
}
public Button getButton(int resId){
return retrieveView(resId);
}
@SuppressWarnings("unchecked")
protected <V extends View> V retrieveView(int viewId){
View view = views.get(viewId);
if(view == null){
view = mItemView.findViewById(viewId);
views.put(viewId,view);
}
return (V) view;
}
public void setText(int resId,CharSequence text){
getTextView(resId).setText(text);
}
public void setText(int resId,int strId){
getTextView(resId).setText(strId);
}
}
以前写Adapter的时候,每一种viewType 都对应了一个ViewHolder,其中有大量的findViewById绑定视图,有了RVBaseViewHolder,再也不需要定义ViewHolder了,通过id获取View就行,View用SparseArray 保存进行了复用,避免每一次都find。
4,RVBaseAdapter
/**
* Created by zhouwei on 17/1/19.
*/
public abstract class RVBaseAdapter<C extends RVBaseCell> extends RecyclerView.Adapter<RVBaseViewHolder>{
public static final String TAG = "RVBaseAdapter";
protected List<C> mData;
public RVBaseAdapter(){
mData = new ArrayList<>();
}
public void setData(List<C> data) {
addAll(data);
notifyDataSetChanged();
}
public List<C> getData() {
return mData;
}
@Override
public RVBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
for(int i=0;i<getItemCount();i++){
if(viewType == mData.get(i).getItemType()){
return mData.get(i).onCreateViewHolder(parent,viewType);
}
}
throw new RuntimeException("wrong viewType");
}
@Override
public void onBindViewHolder(RVBaseViewHolder holder, int position) {
mData.get(position).onBindViewHolder(holder,position);
}
@Override
public void onViewDetachedFromWindow(RVBaseViewHolder holder) {
super.onViewDetachedFromWindow(holder);
Log.e(TAG,"onViewDetachedFromWindow invoke...");
//释放资源
int position = holder.getAdapterPosition();
//越界检查
if(position<0 || position>=mData.size()){
return;
}
mData.get(position).releaseResource();
}
@Override
public int getItemCount() {
return mData == null ? 0:mData.size();
}
@Override
public int getItemViewType(int position) {
return mData.get(position).getItemType();
}
/**
* add one cell
* @param cell
*/
public void add(C cell){
mData.add(cell);
int index = mData.indexOf(cell);
notifyItemChanged