笔者使用RecycleView也有一段时间了,每次遇到需要为RecycleView添加头尾布局的时候,都会是一阵头疼。之前我的做法是直接给依赖的实体集合添加头尾类型的Item Data。打脸的说,这是在赶需求的一种无赖做法。得益于最近项目不是很紧张,所以抽个周末的时间好好研究一下这一块的知识。 言归正传,本篇文章我要实现的效果是简洁高效地为RecycleView无限增加头尾布局。
1 具体思路 1、新建一个HeaderFooterWrapAdapter装饰类,它继承于RecycleView.Adapter,负责拓展普通RecycleView.Adapter对象(被装饰对象)的功能。 2、在onCreateViewHolder、onBindViewHolder、getItemViewType、getItemCount这几个方法区分有无头尾布局。如果没有头尾布局,一律按照被装饰对象的逻辑处理。 3、在onAttachedToRecyclerView方法内,做好GridView的适配。
2 下面我们就把以上的思路转化成可爱的代码吧 一、我们应该让HeaderFooterWrapAdapter继承自RecycleView.Adapter,并且定义好需要的成员对象。
1 2 3 4 5 6 public class HeaderFooterWrapAdapter extends RecyclerView.Adapter { RecyclerView.Adapter adapter;//被装饰对象 private List<View> headerViews = new ArrayList<>(); private List<View> footerViews = new ArrayList<>(); …… }
二、重写RecyclerView.Adapter中几个重要的方法。 1、判断item个数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Override public int getItemCount() { if (headerViews.size() != 0 && footerViews.size() != 0)//同时加了头部和尾部 { return adapter.getItemCount() + headerViews.size() + footerViews.size(); } else if (headerViews.size() != 0) { //只有头部 return adapter.getItemCount() + headerViews.size(); } else if (footerViews.size() != 0) //只有尾部 { return adapter.getItemCount() + footerViews.size(); } else { return adapter.getItemCount(); } }
2、区分Item类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Override public int getItemViewType(int position) { if (headerViews.size() != 0) { if (position >= 0 && position < headerViews.size()) { return headerViews.get(position).hashCode(); } } if (footerViews.size() != 0) { int i = position - headerViews.size() - adapter.getItemCount(); if (i >= 0) return footerViews.get(i).hashCode(); } return 0; }
3、创建Item。
1 2 3 4 5 6 7 8 9 10 11 12 13 public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { for (View headerView : headerViews) { if (headerView.hashCode() == viewType) { return new HeaderViewHolder(headerView); } } for (View footerview : footerViews) { if (footerview.hashCode() == viewType) { return new FooterViewHolder(footerview); } } return adapter.onCreateViewHolder(parent, viewType); }
4、绑定数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { if (headerViews.size() != 0 && footerViews.size() != 0)//同时加了头部和尾部 { if (position >= headerViews.size() && position < headerViews.size() + adapter.getItemCount()) { adapter.onBindViewHolder(holder, position - headerViews.size()); } } else if (headerViews.size() != 0) { //只有头部 if (position >= headerViews.size()) { adapter.onBindViewHolder(holder, position - headerViews.size()); } } else if (footerViews.size() != 0) //只有尾部 { if (position >= 0 && position < adapter.getItemCount()) { adapter.onBindViewHolder(holder, position); } } else { adapter.onBindViewHolder(holder, position); } }
三、接下来适配GridView,这里主要是通过GridLayoutManager的setSpanSizeLookup方法动态处理头尾布局。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Override public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { ((GridLayoutManager) layoutManager).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { if (headerViews.size() != 0 && footerViews.size() != 0) { if (position >= 0 && position < headerViews.size()) { return ((GridLayoutManager) layoutManager).getSpanCount(); } else if (position >= getItemCount() - footerViews.size() && position < getItemCount()) { return ((GridLayoutManager) layoutManager).getSpanCount(); } else { return 1; } } else if (headerViews.size() != 0) { if (position >= 0 && position < headerViews.size()) { return ((GridLayoutManager) layoutManager).getSpanCount(); } return 1; } else if (footerViews.size() != 0) { if (position >= getItemCount() - footerViews.size() && position < getItemCount()) { return ((GridLayoutManager) layoutManager).getSpanCount(); } return 1; } return 1; } }); } }
四、定义添加头部和尾部布局的公开方法。
1 2 3 4 5 6 7 8 9 10 //添加头布局 public void addHeaderView(View headerView) { this.headerViews.add(headerView); notifyItemInserted(headerViews.size() - 1); } //添加尾布局 public void addFooterView(View footerView) { this.footerViews.add(footerView); notifyItemInserted(headerViews.size() + adapter.getItemCount() + footerViews.size() - 1); }
好了,以上就是为RecycleView添加头尾布局的核心代码。调用方式也很简单,伪代码如下。
1 2 3 4 5 6 7 8 9 10 MyAdapter myAdapter=....; .... HeaderFooterWrapAdapter headerFooterWrapAdapter=new HeaderFooterWrapAdapter(myAdapter); headerFooterWrapAdapter.addFooterView(footerView01); headerFooterWrapAdapter.addFooterView(footerView02); headerFooterWrapAdapter.addFooterView(footerView03); headerFooterWrapAdapter.addHeaderView(headerView01); headerFooterWrapAdapter.addHeaderView(headerView02); headerFooterWrapAdapter.addHeaderView(headerView03); .......
这里有一点需要注意就是Inflate创建headerView或者footerView时,parent需要传入recyclerView对象才能时布局的顶层属性起效果,例如:
1 2 View footerView = LayoutInflater.from(this).inflate(R.layout.footerview, rcv, false);
关于inflate的使用技巧,可以参考一下这篇文章 。最后,再贴一张实现的效果图片吧~
参考资料 学会自己给RecyclerView添加Header、Footer和加载更多回调