转载请注明出处 http://blog.csdn.net/u011453163/article/details/58586687
RecyclerView 已经不是一个陌生的组件了,但是相对于现在的项目在还是用比较老旧的ListView,虽然功能都能实现,但是毕竟得跟上时代变化嘛。
关于RecyclerView添加 头部和尾部,网上已经有各种大牛的版本,实现起来也不是很难。但是看一遍不如撸一遍。这里对学习RecyclerView过程中做一些记录,部分实现也有借鉴大牛们的思路,见笑。
先上个简单的效果图。
RecyclerView不像ListView 一样 提供了添加头部和尾部的方法,但是RecyclerView和ListView一样是支持多类型Item布局的,那么反过来 头部和尾部 也就只是一些特定类型的布局而已。所以我们可以通过 public int getItemViewType(int position) 算出头部和尾部的Type 来加载头部和尾部。
这里通过两种方式来实现这个效果,思路是一样的。
自定义一个 ExpandAdapter 继承 RecyclerView.Adapter
public class ExpandAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { PRivate RecyclerView.Adapter<RecyclerView.ViewHolder> adapter; public ExpandAdapter(RecyclerView.Adapter<RecyclerView.ViewHolder> adapter) { this.adapter = adapter; } @Override public int getItemViewType(int position) { return adapter.getItemViewType(position); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return adapter.onCreateViewHolder(parent, viewType); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { adapter.onBindViewHolder(holder, position); } @Override public int getItemCount() { return adapter.getItemCount(); } }这里只是简单的做一个转换工作,以方便后期使用不需要去改动太多的东西。 1 通过 List 来实现。 接下来开始分析实现思路,首先要注意的是 public int getItemViewType(int position)默认是返回 0 的, 所以在定义头部和尾部的type是要错开 0 ,网上大部分大牛的思路都是定义一个比较大的起始值。
1 一个较大的起始值
private static final int TPEY_COUNT_START_TAG =100000;2 两个存储头部和尾部 view的集合,和添加头部和尾部对应的方法
List<View> headViews = new ArrayList<>(); List<View> footViews = new ArrayList<>(); public void addHeadView(View v) { if (headViews != null) { v.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); headViews.add(v); } } public void addFootView(View v) { if (footViews != null) { v.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); footViews.add(v); } }3 在 public int getItemViewType(int position) 计算返回的type
public int getItemViewType(int position) { if (headViews.size() > 0) { if (position < headViews.size()) { return position+ TPEY_COUNT_START_TAG; } } if (footViews.size() > 0) { if (position >= getItemCount() - footViews.size()) { return position+ TPEY_COUNT_START_TAG; } } return adapter.getItemViewType(position); }因为这里只有一个position参数可以使用,所以在position的基础上加个一个较大的值,这里是加法之后我们要做减法来算出下标 取出对应的 头部view和尾部view。
4 从List中取出渲染对应的头部和尾部。
定义头部和尾部对应的ViewHolder 便于理解
class HeaderHolder extends RecyclerView.ViewHolder { public HeaderHolder(View itemView) { super(itemView); } } class FootHolder extends RecyclerView.ViewHolder { public FootHolder(View itemView) { super(itemView); } }@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (headViews.size() > 0) { if (viewType- TPEY_COUNT_START_TAG >= 0 && viewType- TPEY_COUNT_START_TAG < headViews.size()) { return new HeaderHolder(headViews.get(viewType- TPEY_COUNT_START_TAG)); } } if (footViews.size() > 0) { Log.d("ExpandAdapter", "viewType:" + viewType); if (viewType- TPEY_COUNT_START_TAG >= getItemCount() - footViews.size() && viewType- TPEY_COUNT_START_TAG < getItemCount()) { return new FootHolder(footViews.get(viewType- TPEY_COUNT_START_TAG -adapter.getItemCount()-headViews.size())); } } return adapter.onCreateViewHolder(parent, viewType); }这里主要就是做一些判断 算出下标 来取出 headViews, footViews中对应的view。
5 绑定数据
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if(holder instanceof FootHolder ||holder instanceof HeaderHolder){ return; } adapter.onBindViewHolder(holder, position); } @Override public int getItemCount() { return adapter.getItemCount() + headViews.size() + footViews.size(); }动态添加的头部和尾部不需要在适配器里渲染数据,所以直接返回不管。Item总数就是头部和尾部和普通itme的总和,这个没必要说的。
6.由于RecyclerView 的布局形式不同。GridLayoutManager,StaggeredGridLayoutManager,LinearLayoutManager。为了适配这三者,还需要重写两个方法,这个是借鉴网上大牛的。
@Override public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { adapter.onViewAttachedToWindow(holder); int position = holder.getLayoutPosition(); if ((position >= 0 && position < headViews.size())||(position>=getItemCount()-footViews.size()&&position<getItemCount())) { ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); if (lp != null && lp instanceof StaggeredGridLayoutManager.LayoutParams) { StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp; p.setFullSpan(true); } } } @Override public void onAttachedToRecyclerView(final RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); if (recyclerView.getLayoutManager() instanceof GridLayoutManager) { ((GridLayoutManager) recyclerView.getLayoutManager()).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { if (position >= 0 && position < headViews.size()) { return ((GridLayoutManager) recyclerView.getLayoutManager()).getSpanCount(); }else if(position>=getItemCount()-footViews.size()&&position<getItemCount()){ return ((GridLayoutManager) recyclerView.getLayoutManager()).getSpanCount(); } else { return 1; } } }); } }StaggeredGridLayoutManager
GridLayoutManager
以上是使用List实现的方式,第二种的思路也是一样的,通过两个HashMap来存储头部和尾部 ,就不详细介绍了,主要列出一些区别。
private static final int TPEY_HEADER_STATRT_INDEX =-100000; private static final int TPEY_FOOT_START_INDEX =100000; HashMap<Integer,View> headViewss=new HashMap<>(); HashMap<Integer,View> footViewss=new HashMap<>(); /**添加头部*/ public void addHeadView(View v) { if(headViewss!=null){ v.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); headViewss.put(TPEY_HEADER_STATRT_INDEX+headViewss.size(),v); } } /**添加头部*/ public void addFootView(View v) { if(footViewss!=null){ v.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); footViewss.put(TPEY_FOOT_START_INDEX+footViewss.size(),v); } }/**重写适配器的两个方法也有区别 这里也可以同set 返回key值*/ @Override public int getItemViewType(int position) { if(headViewss!=null&&position<headViewss.size()){ return position+TPEY_HEADER_STATRT_INDEX; } if(footViewss!=null&&position>=adapter.getItemCount()+headViewss.size()){ return TPEY_FOOT_START_INDEX+(position-adapter.getItemCount()-headViewss.size()); } return adapter.getItemViewType(position); } /**通过Key直接获取*/ @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if(headViewss.containsKey(viewType)){ return new HeaderHolder(headViewss.get(viewType)); } if(footViewss.containsKey(viewType)){ return new FootHolder(footViewss.get(viewType)); } return adapter.onCreateViewHolder(parent, viewType); }/**此方法基本一样*/ @Override public int getItemCount() { return adapter.getItemCount() + headViewss.size() + footViewss.size(); }这里因为是使用了ExpandAdapter 来做一个包装,所以相应的使用一个自定义RecyclerView来实现。以便我们不用做太多的改动。
public class ExpandRecyclerView extends RecyclerView{ ExpandAdapter expandAdapter; public ExpandRecyclerView(Context context) { super(context); } public ExpandRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public ExpandRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public void setAdapter(Adapter adapter) { expandAdapter =new ExpandAdapter(adapter); super.setAdapter(expandAdapter); } public void addHeadView(View v){ if(expandAdapter !=null){ expandAdapter.addHeadView(v); expandAdapter.notifyDataSetChanged(); } } public void addFootView(View v){ if(expandAdapter !=null){ expandAdapter.addFootView(v); expandAdapter.notifyDataSetChanged(); } }}使用自定义RecyclerView之后 我们不需要去改动适配器 还是该怎么使用就怎么使用。
到此,RecyclerView的添加头部和尾部就结束了。有什么不对的地方望指正。
新闻热点
疑难解答