学习MultiViewAdapter——2

本文介绍了一个国外开源项目MultiViewAdapter的使用方法,该适配器能够简化多布局RecyclerView的实现过程,无需使用if-else语句即可实现多类型布局。

本文讲到的关于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);
        }
    }
}

效果图来自作者Wiki


这是一个非常简单的多布局实现,我们现在要去分析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的内容。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值