上一节咱们讲到了 Android 5.X新特性之RecyclerView基本解析及无限复用 相信你们也应该熟悉了RecyclerView的基本使用,这一节咱们来学习下,为RecyclerView添加HeaderView和FooterView。html
针对RecyclerView的头部和底部,官方并无给咱们提供像listView同样能够直接经过addHeaderView()/addFooterView()的方法,因此只能靠咱们本身去实现了,那怎么实现呢?你们都知道RecyclerView已经为咱们封装好了Adapter和ViewHolder,在Adapter中咱们须要重写onCreateViewHolder(ViewGroup parent, int viewType)这个方法方便咱们把ItemView布局文件或是自定义View传递到ViewHolder中,从而达到ItemView的重复使用和回收等。而咱们今天要讲的添加头部和底部和该方法有密不可分的关系。python
你们仔细观察onCreateViewHolder(ViewGroup parent, int viewType)这个方法,在它的参数中,含有一个viewType,它就表明了每个子列表中的ItemView的类型,而该类型咱们又能够经过Adapter中封装好的getItemViewType()方法来定义。所以,咱们能够根据这两个方法来完成咱们今天的学习。微信
首先,咱们也须要在BaseRecyclerAdapter中添加一个addHeaderView的方法,用于接受Activity中传递过来的HeaderView,而且把HeaderView添加到第0个ItemView中。ide
private View mHeaderView; public void addHeaderView(View headerView){ mHeaderView = headerView; notifyItemInserted(0); }
而后,咱们在咱们自定义的BaseRecyclerAdapter中重写getItemViewType()方法,而且定义两个静态变量来区分咱们的viewType的类型,以下:布局
public final static int TYPE_HEADER = 0; public final static int TYPE_BODY = 1; @Override public int getItemViewType(int position) { return super.getItemViewType(position); }
ok,如今咱们能够针对getItemViewType()方法来为咱们的ItemView设置viewType类型了。咱们知道,getItemViewType()默认返回的是0这个类型,因此咱们重载该方法,在没有mHeaderView时,咱们让它返回TYPE_BODY这个类型,而当有mHeaderView,因为把它添加到第0个ItemView中了,因此咱们能够根据position等于0的时候让它返回TYPE_HEADER这个类型。定义好的类型将会在onCreateViewHolder()中使用到。学习
因此咱们的getItemViewType方法能够这样设计:this
@Override public int getItemViewType(int position) { if(mHeaderView == null) return TYPE_BODY; if(position == 0) { return TYPE_HEADER; } return TYPE_BODY; }
到这里你们已经很明确的知道了每一个ItemView的viewType类型了,那么咱们就能够在onCreateViewHolder()方法中根据ItemView的viewType来作出不一样的判断了,以下:设计
@Override public BaseViewHolderHelper onCreateViewHolder(ViewGroup parent, int viewType) { if(viewType == TYPE_HEADER && mHeaderView != null){ return new BaseViewHolderHelper(mHeaderView); } View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutResId, parent, false); return new BaseViewHolderHelper(view); }
代码一目了然,就是根据viewType判断是否为TYPE_HEADER,若是是,则添加不一样的布局或View,不然,加载正常的布局,其余的不变。code
而后,咱们就能够在onBindViewHolder(BaseViewHolderHelper holder, int position)方法中根据当前的position位置来绑定咱们要显示数据了。htm
@Override public void onBindViewHolder(BaseViewHolderHelper holder, int position) { if(getItemViewType(position) == TYPE_HEADER){ return; }else{ if(mHeaderView != null){ position-- ; } holder.itemView.setTag(position); holder.itemView.setOnClickListener(this); holder.itemView.setOnLongClickListener(this); T itemData = mDatas.get(position); displayContents(holder,itemData); } }
代码解释:若是当前position位置的类型是TYPE_HEADER,也就是说用来显示mHeaderView的,这里咱们就直接返回mHeaderView的布局,不作事件处理了;若是不是,而且RecyclerView是有带mHeaderView头部的,那么因为它占去第0个itemView,因此咱们的position是从第一个开始计算的,因此咱们必须获得当前真实position位置,并经过position位置来获取当前的真实数据,若是不带mHeaderView头部,则可直接根据position获取显示数据,其余的逻辑不变。
还有注意的是当mHeaderView不为空时,咱们的数据量大小也有必定的变化,请看:
@Override public int getItemCount() { return mHeaderView != null ? mDatas.size() + 1 : mDatas.size(); }
ok,在RecycerActivity的onCreate方法中添加一下两句:
View headerView = LayoutInflater.from(this).inflate(R.layout.item_view1, null); mBaseRecyclerAdapter.addHeaderView(headerView);
来看看运行结果吧
ok,已经完成了mHeaderView 的添加。可是有个小问题,当你把RecyclerView的布局设置为GridLayoutManager时,如:mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));就会出现这种状况:
这种状况也很好解决,在GridLayoutManager中咱们能够在SpanSizeLookup中从新设置显示的列数。
@Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); RecyclerView.LayoutManager manager = recyclerView.getLayoutManager(); if(manager instanceof GridLayoutManager){ final GridLayoutManager gridLayoutManager = ((GridLayoutManager) manager); gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { //是HeaderView则占全部列,不然只占本身列 return getItemViewType(position) == TYPE_HEADER ? gridLayoutManager.getSpanCount() : 1; } }); } }
最主要的就是在getSpanSize方法中根据咱们的须要从新设置就ok了。看看吧
好了,mHeaderView 已基本搞定,如今来看看怎么添加FooterView了,其实原理是同样的,也是根据getItemViewType()返回的ViewType类型来加载不一样的布局了。
首先咱们须要定义一个内部类FooterViewHolder继承咱们的BaseViewHolderHelper,它主要是用来绑定FooterView布局文件:
private class FooterViewHolder extends BaseViewHolderHelper{ private TextView footView; public FooterViewHolder(View itemView) { super(itemView); footView = (TextView) itemView.findViewById(R.id.tv_addFooter); } }
而后在getItemViewType中获取到最后的ItemView的位置并返回TYPE_FOOTER类型:
@Override private View mFooterView; public final static int TYPE_FOOTER = 2; ...... public int getItemViewType(int position) { if(position + 1 == getItemCount()){ return TYPE_FOOTER; } if(mHeaderView == null) return TYPE_BODY; if(position == 0) { return TYPE_HEADER; } return TYPE_BODY; }
另外在getItemCount()方法中咱们由于添加个一个FooterView因此须要在原来的基础上再加 1 ;
@Override public int getItemCount() { return mHeaderView != null ? mDatas.size() + 2 : mDatas.size() + 1; }
再次在onCreateViewHolder方法中根据类型加载不一样的布局文件:
@Override public BaseViewHolderHelper onCreateViewHolder(ViewGroup parent, int viewType) { ... if(viewType == TYPE_FOOTER){ mFooterView = LayoutInflater.from(mContext).inflate(R.layout.custom_footerview, parent,false); return new FooterViewHolder(mFooterView); } View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutResId, parent, false); return new BaseViewHolderHelper(view); }
最后在onBindViewHolder方法中来展现咱们的数据吧
@Override public void onBindViewHolder(BaseViewHolderHelper holder, int position) { if(getItemViewType(position) == TYPE_HEADER){ return; }else if(getItemViewType(position) == TYPE_FOOTER){ FooterViewHolder footViewHolder=(FooterViewHolder)holder; footViewHolder.footView.setText("上拉加载更多..."); } else{ ... } }
ok,完成,来看看结果吧
总结下,在给RecyclerView添加HeaderView和FooterView时,只要利用好getItemViewType这个方法,返回相对应的ViewType,而且在onCreateViewHolder方法中根据ViewType类型加载不一样的布局就彻底但是实现咱们的需求了。提及来就是这么简单。好了,今天就讲到这里吧,祝你们学习愉快。
更多资讯请关注微信平台,有博客更新会及时通知。爱学习爱技术。