简介
BRAVH是一个recyclerView的adapter,能够快速适配多种类型adapter,可定制,用的人挺多,下面我们就来分析分析他的源码,模拟来写一个我们的adapter。
我们将recyclerView的adapter与自定义viewholder联系在一起,使用了BaseQuickAdapter<T, K extends BaseViewHolder>来作为recycler.adapter
先看BRAVH的BaseViewHolder类
BaseViewHolder extends RecyclerView.ViewHolder
继承自ViewHolder,里面setXXX方法全是由itemview里面的view调用方法实现 存放了一个SparseArray<View> views成员变量用来初始化或者第一次遍历存放引用,添加快捷操作,省去下一次findview的时间
接下来看BaseQuickAdapter类
BaseQuickAdapter<T, K extends BaseViewHolder> extends RecyclerView.Adapter<K>
将K泛型传入给RecyclerView.Adapter作为viewholder T泛型分析: 传入的数据集List<T>的类型,用来绑定数据
自定义Adapter,ViewHolder
我们也来模拟一个adapter类型,使用自定义继承viewHolder的MyViewHolder作为Viewholder
这样我们就有了一个adapter,里面的Viewholder由外部传进来,我们可以继承该Viewholder自己做快捷操作,让外部继承实现MyViewholder逻辑,同BaseViewHolder,他也只是对itemview引用设置子View参数,这部分基本忽略。我们看onCreateViewHolder->VH createViewHolder(View view)这个方法,这是一个泛型类的实例化,这个直接上代码见git。
给Item添加加载动画
onViewAttachedToWindow(VH holder)方法: 每次Viewholder添加到window的时候contentView开始动画可以制作item加载效果 想要控制只让itemView进行一次动画,BRAVH里面设置了一个标志位,我们也写一个AnimOnce来做这个标志位,每次加载的时候得到viewholder的位置,并与上一次的加载过的位置比较,如果小,代表当前位置的contentview是新的,需要开启动画,否则如果AnimOnce只要一次,不开启动画,因为动画已经之前被加载过,我们可以这样实现:
这样,就能添加我们的itemview动画,如果需要外部定制,我们可以修改我们的animateView(View root)方法,给里面添加一个animation动画,并且提供外部接口,比如:
关于动画,可以使用animator或者animation都可以的,只要定制animateView方法
添加头部尾部空数据的布局
我们看BRAVH如何实现,
下面我们也来模拟一个头部尾部布局~ 写一个方法来添加头部尾部视图
GetItemType返回对应Type
根据对于Type构造Viewholder
根据position位置来绑定viewholder数据
这样我们的头部尾部布局就添加好了,添加loading布局与空布局也是一个道理,只是多加载了一种类型而已
加载更多的实现
这是在OnBindViewHolder->position判断位置 如果position已经在最后的位置,那么触发加载更多 下面我们可以写一个加载更多的方法:
实现拖拽,滑动删除
BRAVH是怎么实现呢?官方这样使用:
我们可以看到使用的类是ItemDragAndSwipeCallback ,这里面需要传入adapter需要BaseItemDraggableAdapter类型,我们进入ItemDragAndSwipeCallback 发现其实只是BaseItemDraggableAdapter回调用,其实本身并没有用到任何BaseItemDraggableAdapter属性。所以可以做出一个提取的过程,把传入类型BaseItemDraggableAdapter修改成interface,只要RecyclerAdapter实现这个接口就可以了,这样不必非要传入BaseItemDraggableAdapter类型。
这部分也可以直接用原生ItemTouchHelper,复写onMove实现item交换,onSwiped实现Item删除 我们开始写交换逻辑:
下面我们实现删除逻辑:
自定义使用不同的Item类型 现在默认item类型有header,footer,loading,empty,default。如果想要自定义类型,那么我们可以修改自定义的adapter,在getItemType返回default类型的时候,使用抽象方法让子类实现,修改adapter为抽象类,这将影响:
这样我们便能够自定义itemType
getItemtype 使用抽象方法getDefItemViewType(int realDataPos)
暴露抽象方法onCreateDefViewHolder(ViewGroup parent, int viewType)
回顾的时候发现还是返回VH类型的Viewholder容易定制,这样自定义实现就不需要泛型实例化方法 abstract VH onCreateDefViewHolder(ViewGroup parent, int viewType);
暴露抽象方法onBindDefViewHolder(VH holder, int realDataPos)
我们的ItemType是由传入的数据类型决定的,可以定义一个接口,让传入的数据类型实现该接口并且实现getItemType
这样就可以在外部自定义数据类型了
添加分组
首先我们看BRAVH怎么介绍
Stop,我们到这边似乎不必要去看他怎么实现了,我猜原理应该和上面添加自定义类型是一样的,上面更加广泛,所以这里我们只有2种类型而已,一种分组头类型。一种分组内容类型。 我们开始编写代码试试:
我们先写一个数据集类型实现ItemType接口
这样添加分组就是2种自定义数据类型而已,我们可以写一个实体类,返回2种类型,使用MultiAdapter适配,实现方法:
分组的伸缩栏
既然要实现分组,我的思路是这样的
可是设计主实体类或许有些麻烦了,要求里面有个子数据集类型是实体类类型的: 我们先定义接口
public interface Expandable<E> extends ItemType{
List<E> subItems();
boolean isExpandable();
void setExpand(boolean expand);
boolean isExpand();
}
这个接口要求子数据级,是否可以扩展
然后定义数据类型
public class ExpandEntity implements Expandable<ExpandEntity>{
public final static int SUB1 = 0x111;
public final static int SUB2 = 0x112;
// public final static int SUB3 = 0x113;
private int itemType;
private List<ExpandEntity> subDatas;
private String values;
public ExpandEntity(int itemType, List<ExpandEntity> subDatas,String values) {
this.itemType = itemType;
this.subDatas = subDatas;
this.values = values;
}
public String getValues() {
return values;
}
@Override
public int getItemType() {
return itemType;
}
@Override
public List<ExpandEntity> subItems() {
return subDatas;
}
@Override
public boolean isExpandable() {
return subDatas != null && subDatas.size() > 0;
}
boolean isExpand = false;
@Override
public void setExpand(boolean expand) {
isExpand = expand;
}
@Override
public boolean isExpand() {
return isExpand;
}
}
这样就可以实现该接口,外部类自由继承,然后自由添加values属性
下面我们的接口实体类好了,需要制作adapter,可是adapter怎么写呢?我们需要传入的数据类型为Expandable类型,而且数据集合类型要是Expandable的实现类,是否可以这样写?
class ExpandAdapter<I extends Expandable<I>,VH extends MyViewHolder> extends BackQuickAdapter<I,VH>
这样保证数据集市I类型,I又是Expandable类型,这样可以遍历I的子数据集实现多级的展开与隐藏
Adapter具体实现该怎样呢? 我们可以在onBindDefViewHolder方法里面添加itemview的点击事件,然后为itemview添加tag,tag里面是绑定的数据,再实现点击事件的时候取出tag里面数据,判断单项是否可以展开,如果可以展开,得到子数据集放入主数据集中,然后notifyItemRangeInserted就能实现数据的多级展开
注意这里移除操作,去除datas中间的数据,实现折叠效果,然后notifyItemRangeMoved 这部分的逻辑也是对主数据集datas操作,这里没有具体实现,有心的小伙伴可以看BRAVH的expand与collapse方法,人家的折叠可是多级折叠的,将所有子集都移除然后notifyItemRangeMoved。这里我就不写这部分逻辑代码了。
我是分割线 这里我们写Adapter需要涉及到一个泛型的实例化,因为需要将View加入Viewholder,并把Viewholder实例化,而Viewholder又是VH类型的,所以下面方法我直接贴上代码
/**
* 抽象类反射实例化
* @param view
* @return
*/
protected VH createViewHolder(View view) {
Class temp = getClass();
Class z = null;
while (z == null && null != temp) {
z = getInstancedGenericKClass(temp);
temp = temp.getSuperclass();
}
VH k;
// 泛型擦除会导致z为null
if (z == null) {
k = (VH) new MyViewHolder(view);
} else {
k = createGenericKInstance(z, view);
}
return k != null ? k : (VH) new MyViewHolder(view);
}
/**
* try to create Generic K instance
*
* @param z
* @param view
* @return
*/
@SuppressWarnings("unchecked")
private VH createGenericKInstance(Class z, View view) {
try {
Constructor constructor;
// inner and unstatic class
if (z.isMemberClass() && !Modifier.isStatic(z.getModifiers())) {
constructor = z.getDeclaredConstructor(getClass(), View.class);
constructor.setAccessible(true);
return (VH) constructor.newInstance(this, view);
} else {
constructor = z.getDeclaredConstructor(View.class);
constructor.setAccessible(true);
return (VH) constructor.newInstance(view);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
/**
* get generic parameter K
*
* @param z
* @return
*/
private static Class getInstancedGenericKClass(Class z) {
Type type = z.getGenericSuperclass();
if (type instanceof ParameterizedType) {
Type[] types = ((ParameterizedType) type).getActualTypeArguments();
for (Type temp : types) {
if (temp instanceof Class) {
Class tempClass = (Class) temp;
if (MyViewHolder.class.isAssignableFrom(tempClass)) {
return tempClass;
}
}
}
}
return null;
}
这样泛型的实例化就完成了~
总结
我们在写Adapter的时候。将数据集类型以泛型的形式传入。 在Adapter中抽象出onBindViewHolder,onCreateViewHolder,除了处理预置的类型,比如头布局,尾布局,空布局,loading布局。其他的都需要自定义ItemType数据类型来实现定制view。 扩展与折叠也是属于自定义数据类型中的一种,但是要求传入的数据集类型T中还有T类型的子集合,用来得到子集,这样可以保持与Adapter 的数据类型一直,用于展开删除其实就是对Adapter中的数据集datas插入与删除然后通知刷新而已 拖拽与滑动删除默认ItemTouhHelper,在onmoved与onswipe中交换数据集中的位置或者删除某个位置来通知刷新
Source源代码