zoukankan      html  css  js  c++  java
  • Android 5.X新特性之为RecyclerView添加HeaderView和FooterView

    上一节我们讲到了 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中。

    	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类型加载不同的布局就完全可是实现我们的需求了。说起来就是这么简单。好了,今天就讲到这里吧,祝大家学习愉快。

    更多资讯请关注微信平台,有博客更新会及时通知。爱学习爱技术。

    这里写图片描述

  • 相关阅读:
    Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.
    DHCP "No subnet declaration for xxx (no IPv4 addresses)" 报错
    Centos安装前端开发常用软件
    kubernetes学习笔记之十:RBAC(二)
    k8s学习笔记之StorageClass+NFS
    k8s学习笔记之ConfigMap和Secret
    k8s笔记之chartmuseum搭建
    K8S集群集成harbor(1.9.3)服务并配置HTTPS
    Docker镜像仓库Harbor1.7.0搭建及配置
    Nginx自建SSL证书部署HTTPS网站
  • 原文地址:https://www.cnblogs.com/guanmanman/p/6092378.html
Copyright © 2011-2022 走看看