Android 5.X新特性之为RecyclerView添加HeaderView和FooterView
2016-05-09 18:27
591 查看
上一节我们讲到了 Android 5.X新特性之RecyclerView基本解析及无限复用 相信大家也应该熟悉了RecyclerView的基本使用,这一节我们来学习下,为RecyclerView添加HeaderView和FooterView。
针对RecyclerView的头部和底部,官方并没有给我们提供像listView一样可以直接通过addHeaderView()/addFooterView()的方法,所以只能靠我们自己去实现了,那怎么实现呢?大家都知道RecyclerView已经为我们封装好了Adapter和ViewHolder,在Adapter中我们需要重写onCreateViewHolder(ViewGroup parent, int viewType)这个方法方便我们把ItemView布局文件或是自定义View传递到ViewHolder中,从而达到ItemView的重复使用和回收等。而我们今天要讲的添加头部和底部和该方法有密不可分的关系。
大家仔细观察onCreateViewHolder(ViewGroup parent, int viewType)这个方法,在它的参数中,含有一个viewType,它就代表了每一个子列表中的ItemView的类型,而该类型我们又可以通过Adapter中封装好的getItemViewType()方法来定义。因此,我们可以根据这两个方法来完成我们今天的学习。
首先,我们也需要在BaseRecyclerAdapter中添加一个addHeaderView的方法,用于接受Activity中传递过来的HeaderView,并且把HeaderView添加到第0个ItemView中。
然后,我们在我们自定义的BaseRecyclerAdapter中重写getItemViewType()方法,并且定义两个静态变量来区分我们的viewType的类型,如下:
ok,现在我们可以针对getItemViewType()方法来为我们的ItemView设置viewType类型了。我们知道,getItemViewType()默认返回的是0这个类型,所以我们重载该方法,在没有mHeaderView时,我们让它返回TYPE_BODY这个类型,而当有mHeaderView,由于把它添加到第0个ItemView中了,所以我们可以根据position等于0的时候让它返回TYPE_HEADER这个类型。定义好的类型将会在onCreateViewHolder()中使用到。
所以我们的getItemViewType方法可以这样设计:
到这里大家已经很明确的知道了每个ItemView的viewType类型了,那么我们就可以在onCreateViewHolder()方法中根据ItemView的viewType来做出不同的判断了,如下:
代码一目了然,就是根据viewType判断是否为TYPE_HEADER,如果是,则添加不同的布局或View,否则,加载正常的布局,其他的不变。
然后,我们就可以在onBindViewHolder(BaseViewHolderHelper holder, int position)方法中根据当前的position位置来绑定我们要显示数据了。
代码解释:如果当前position位置的类型是TYPE_HEADER,也就是说用来显示mHeaderView的,这里我们就直接返回mHeaderView的布局,不做事件处理了;如果不是,并且RecyclerView是有带mHeaderView头部的,那么由于它占去第0个itemView,所以我们的position是从第一个开始计算的,所以我们必须得到当前真实position位置,并通过position位置来获取当前的真实数据,如果不带mHeaderView头部,则可直接根据position获取显示数据,其他的逻辑不变。
还有注意的是当mHeaderView不为空时,我们的数据量大小也有一定的变化,请看:
ok,在RecycerActivity的onCreate方法中添加一下两句:
来看看运行结果吧
ok,已经完成了mHeaderView 的添加。但是有个小问题,当你把RecyclerView的布局设置为GridLayoutManager时,如:mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));就会出现这种情况:
这种情况也很好解决,在GridLayoutManager中我们可以在SpanSizeLookup中重新设置显示的列数。
最主要的就是在getSpanSize方法中根据我们的需要重新设置就ok了。看看吧
好了,mHeaderView 已基本搞定,现在来看看怎么添加FooterView了,其实原理是一样的,也是根据getItemViewType()返回的ViewType类型来加载不同的布局了。
首先我们需要定义一个内部类FooterViewHolder继承我们的BaseViewHolderHelper,它主要是用来绑定FooterView布局文件:
然后在getItemViewType中获取到最后的ItemView的位置并返回TYPE_FOOTER类型:
另外在getItemCount()方法中我们因为添加个一个FooterView所以需要在原来的基础上再加 1 ;
再次在onCreateViewHolder方法中根据类型加载不同的布局文件:
最后在onBindViewHolder方法中来展示我们的数据吧
ok,完成,来看看结果吧
总结下,在给RecyclerView添加HeaderView和FooterView时,只要利用好getItemViewType这个方法,返回相对应的ViewType,并且在onCreateViewHolder方法中根据ViewType类型加载不同的布局就完全可是实现我们的需求了。说起来就是这么简单。好了,今天就讲到这里吧,祝大家学习愉快。
更多资讯请关注微信平台,有博客更新会及时通知。爱学习爱技术。
针对RecyclerView的头部和底部,官方并没有给我们提供像listView一样可以直接通过addHeaderView()/addFooterView()的方法,所以只能靠我们自己去实现了,那怎么实现呢?大家都知道RecyclerView已经为我们封装好了Adapter和ViewHolder,在Adapter中我们需要重写onCreateViewHolder(ViewGroup parent, int viewType)这个方法方便我们把ItemView布局文件或是自定义View传递到ViewHolder中,从而达到ItemView的重复使用和回收等。而我们今天要讲的添加头部和底部和该方法有密不可分的关系。
大家仔细观察onCreateViewHolder(ViewGroup parent, int viewType)这个方法,在它的参数中,含有一个viewType,它就代表了每一个子列表中的ItemView的类型,而该类型我们又可以通过Adapter中封装好的getItemViewType()方法来定义。因此,我们可以根据这两个方法来完成我们今天的学习。
首先,我们也需要在BaseRecyclerAdapter中添加一个addHeaderView的方法,用于接受Activity中传递过来的HeaderView,并且把HeaderView添加到第0个ItemView中。
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方法可以这样设计:
@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,否则,加载正常的布局,其他的不变。
然后,我们就可以在onBindViewHolder(BaseViewHolderHelper holder, int position)方法中根据当前的position位置来绑定我们要显示数据了。
@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类型加载不同的布局就完全可是实现我们的需求了。说起来就是这么简单。好了,今天就讲到这里吧,祝大家学习愉快。
更多资讯请关注微信平台,有博客更新会及时通知。爱学习爱技术。
相关文章推荐
- Android ORM 框架之 greenDAO
- ViewHolder 的一种更简洁写法
- Android Studio使用过程中遇到的问题
- Android SpannableString 基本用法
- 使用Android SDK自带工具draw9patch制作简单的.9.png图片
- Android5.1权限问题解决
- androidslidingmenu 在android 5.0以上手机里面navigationbar虚拟导航自动弹出布局不会自动变换的问题
- Android 底部Dialog显示
- Android 同步服务器时区
- unable to load class org.codehaus.groovy.runtime.typehandling.shorttypehandling解决方法
- Mstar android按键驱动分析
- Android中使用Handler造成内存泄露的分析和解决
- Android 源码系列之<五>从源码的角度深入理解LayoutInflater.Factory之主题切换(中)
- 转: android apk 防止反编译技术(1~5连载)
- android studio开发NDK模块
- RollViewPager实现android图片轮播效果
- 4000 安卓中搜索本地音乐图片方面详解(音乐图片,切图,画图,描边)
- Android 对话框
- android 屏幕适配
- Android RSA非对称加密解密算法