本文讲到的关于MultiViewAdapter,是学习国外牛人在GitHub上的开源项目。具体的使用方法可以看Wiki。
接着昨天说的内容,昨天讲了其中的一个ItemBinder ,今天要说一下RecyclerAdapter 。
我们先来看看怎么实现一个多布局(代码与图片来自作者博客):
Adapter
//创建多布局的adapter
public class MultiListAdapter extends RecyclerAdapter{
private DataListManager<Bird> birdDataManager;
private DataListManager<Flower> flowerDataManager;
public MultiListAdapter(Context context) {
//将Mode放入DataListManager
birdDataManager = new DataListManager<>(this);
flowerDataManager = new DataListManager<>(this);
//调用父类方法,将ModeManager放入
addDataManager(flowerDataManager);
addDataManager(birdDataManager);
//注册type布局类
registerBinder(
new FlowerBinder(new SimpleDividerDecoration(context, SimpleDividerDecoration.VERTICAL)));
registerBinder(new BirdBinder(new ThickItemDecorator(context)));
}
//添加数据
public void addBirds(List<Bird> dataList) {
birdDataManager.set(dataList);
}
public void addFlowers(List<Flower> dataList) {
flowerDataManager.addAll(dataList);
}
}
ItemBinder
一共两个ItemBinder,我这里贴一个,另一个基本一样。
public class BirdBinder extends ItemBinder<Bird, BirdBinder.ViewHolder> {
BaseViewHolder.OnItemClickListener<Bird> listener;
public BirdBinder(ItemDecorator itemDecorator) {
super(itemDecorator);
}
public BirdBinder(ItemDecorator itemDecorator,
BaseViewHolder.OnItemClickListener<Bird> listener) {
super(itemDecorator);
this.listener = listener;
}
@Override
public BirdBinder.ViewHolder create(LayoutInflater layoutInflater, ViewGroup parent) {
return new ViewHolder(layoutInflater.inflate(R.layout.item_bird, parent, false), listener);
}
@Override
public void bind(ViewHolder holder, Bird item) {
holder.textView.setText(item.getBirdName());
}
@Override
public boolean canBindData(Object item) {
return item instanceof Bird;
}
@Override
public int getSpanSize(int maxSpanCount) {
return maxSpanCount;
}
static class ViewHolder extends BaseViewHolder<Bird> {
private TextView textView;
ViewHolder(View itemView, OnItemClickListener<Bird> listener) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.tv_bird_name);
setItemClickListener(listener);
}
}
}
这是一个非常简单的多布局实现,我们现在要去分析Adapter做了什么?这个Adapter与我们平时写的不一样,没有看到常规的if-else 去判断布局type,所以作者到底怎么去实现这种* 对model的使用无限制,view type可以有自己的span count 或者 ItemDecoration,而且我们还不必去写switch cases 或者 if-else 语句*
1 . RecyclerAdapter
我们看到MultiListAdapter extends RecyclerAdapter
,然后添加了ListData,注册了ItemBinder,最后将数据添加进去。点击去RecyclerAdapter
...省略了一些常量
public final void addDataManager(BaseDataManager dataManager) {
dataManagers.add(dataManager);
}
public final void registerBinder(ItemBinder binder) {
if (!binders.contains(binder)) {
binders.add(binder);
}
}
...省略了一些常用的项点击,拖动事件
这里其实也没有什么,RecyclerAdapter extends CoreRecyclerAdapter
,主要提供了Data与ViewHolder交互的入口。那就看看CoreRecyclerAdapter
吧。
2 . CoreRecyclerAdapter extends RecyclerView.Adapter<ItemViewHolder>
//数据管理器的集合
final List<BaseDataManager> dataManagers = new ArrayList<>();
//UI与数据绑定的集合
final List<ItemBinder> binders = new ArrayList<>();
在代码中我们找到了那两个集合,哦 ,那么拿着这两个集合是怎么做到,不用写if-else就出来MultiView的效果呢?我们接着看,既然继承的是官方的RecyclerView.Adapter
,那我们就跟着它的方法执行顺序看吧。这里只介绍2个覆盖方法,另外两个下一章说:
- getItemCount()
@Override
public final int getItemCount() {
int itemCount = 0;
for (BaseDataManager dataManager : dataManagers) {
itemCount += dataManager.size();
}
return itemCount;
}
从这个方法可以看出遍历dataManagers,然后把各个DataList里面的数据相加,返回给adapter。所以我们可以写多个ItemBinder,也就是一个type一个ItemBinder,这样也可以复用。那么dataManager.size()这里面的数据哪来的呢?下一章讲吧 O(∩_∩)O~
- getItemViewType
@Override
public final int getItemViewType(int adapterPosition) {
//根据adapterPosition,得到ItemBinder
ItemBinder baseBinder = getBinderForPosition(adapterPosition);
if (null != baseBinder) {
return binders.indexOf(baseBinder);
}
return super.getItemViewType(adapterPosition);
}
看到这个关键方法了,这里我们需要知道getItemViewType
默认返回的是0哦,于是通过getBinderForPosition(adapterPosition);
获得itemBinder,然后返回指定元素的索引,看来这个getBinderForPosition
里面有干货,继续看…
ItemBinder getBinderForPosition(int adapterPosition) {
//首先得到数据管理器
BaseDataManager dataManager = getDataManager(adapterPosition);
for (ItemBinder baseBinder : binders) {
//每个ItemBinder都会重写canBindData方法,判断当前的ItemBinder是什么,返回这个ItemBinder
if (baseBinder.canBindData(dataManager.getItem(getItemPositionInManager(adapterPosition)))) {
return baseBinder;
}
}
throw new IllegalStateException("Binder not found for position. Position = " + adapterPosition);
}
//得到数据的manager
BaseDataManager getDataManager(int adapterPosition) {
//判断获取的dataManager是不是可以扩展头和脚的管理器
BaseDataManager dataManager = justGetDataManager(adapterPosition);
if (dataManager instanceof DataGroupManager) {
return ((DataGroupManager) dataManager).getDataManagerForPosition(
getItemPositionInManager(adapterPosition));
} else {
return dataManager;
}
}
BaseDataManager justGetDataManager(int adapterPosition) {
int processedCount = 0;
for (BaseDataManager dataManager : dataManagers) {
processedCount += dataManager.getCount();
//通过adapterPosition可以知道
if (adapterPosition < processedCount) {
return dataManager;
}
}
throw new IllegalStateException("Invalid position for DataManager!");
}
//得到每个manager中屏幕显示item的position
int getItemPositionInManager(int adapterPosition) {
int itemCount;
for (BaseDataManager dataManager : dataManagers) {
itemCount = dataManager.getCount();
Log.w("KIMC", "itemCount = " + itemCount);
Log.w("KIMC", "adapterPosition = " + adapterPosition);
if (adapterPosition - itemCount < 0) {
break;
}
//adapterPosition= adapterPosition- itemCount;
adapterPosition -= itemCount;
}
// adapterPosition now refers to position in manager
return adapterPosition;
}
上面的方法一个套一个,我们一个一个看。
justGetDataManager
主要通过adapterPosition来获取对应的BaseDataManager。具体的做法是:假如我们添加了2个不同类型的type,那么根据adapterPosition与当前dataManager里面的数据,当遍历到第一个dataManagers的时候,与adapterPosition比较,如果小于dataManagers的count就返回当前的dataManagers,遍历第二次dataManagers的时候,与上一个dataManagers里面的数据相加,然后再比较,返回第二dataManagers。
到了getDataManager
中判断了当前dataManager是不是DataGroupManager的。(DataGroupManager主要是处理了有header的Item比如:可以折叠与展开的Item)。
最后在getBinderForPosition
中,获取到DataManager后,经过判断得到想要的itemBinder,getItemPositionInManager
主要处理了position的问题。如果传入了2种不同类型的type,那么遍历第一个dataManagers的时候,与adapterPosition比较,如果adapterPosition-第一次dataManagers的数量小于0的时候,重新遍历。到了第二个dataManagers的时候,adapterPosition - itemCount < 0不成立。adapterPosition的下标从0重新开始,也就是第二种typeView了。也就是随着用户滑动屏幕,position随着type布局的出现与消失而不断的从0-xx来变化。
我们实现多布局的时候,addDataManager()
的先后顺序决定着我们多布局在界面上添加的顺序,当然了,关于addDataManager()
这个方法也提供了重载方法,可以输入index,这样也方便我们开发。
到这里我们也就了解了,为什么不用写if-else就可以实现多布局了。明天准备说一说DataMananger的内容。