• 热门专题

RecyclerView添加头部和尾部布局

作者:  发布日期:2015-07-23 21:28:26
Tag标签:尾部  头部  布局  
  • RecyclerView 出来有很长一段时间了,相信大家对它已经很熟悉了,使用过它的朋友可能都会发现一点,就是 RecyclerView 不能添加 headerView 和 footView,这就让我们有点蛋疼了,也许你会说,没事啊,我们可以重写getItemViewType(int position)这个方法,让他实现多个布局,具体实现如下:

        @Override
        public int getItemViewType(int position) {
            if (position == 0) {
                return HEAD_VIEW;
            }else{
                return BODY_VIEW ;
            }
        }
    

    下面的操作就不多讲了,这种方法相信大家在使用 ListView 的时候就非常熟练; 那既然可以通过以上方法实现,为什么还要写一个可以添加 HeaderView 和 FootView 的 RecyclerView ?其实这个问题不用回答,因为 ListView 也可以通过以上方法实现,但它还是有 addHeaderView和 addFootView 的方法,不过话说回来,通过 addHeaderView 实现的添加头部布局确实有不可代替的功能,比如,ListView 头部图片下拉放大动画;在调用 adapter.notifyDataSetChanged()头部数据是不会重新刷新的等等。

    好了,接下来才是本文的主题内容,如何实现 RecyclerView 添加 HeaderView 和 FootView?

    无从下手?没关系!ListView 不是已经帮我们实现了这样的功能吗,我们可以先看看 ListView 是如何实现的,

        public void addHeaderView(View v, Object data, boolean isSelectable) {
            final FixedViewInfo info = new FixedViewInfo();
            info.view = v;
            info.data = data;
            info.isSelectable = isSelectable;
            mHeaderViewInfos.add(info);
            mAreAllItemsSelectable &= isSelectable;
    
            // Wrap the adapter if it wasn't already wrapped.
            if (mAdapter != null) {
                if (!(mAdapter instanceof HeaderViewListAdapter)) {
                    mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
                }
    
                // In the case of re-adding a header view, or adding one later on,
                // we need to notify the observer.
                if (mDataSetObserver != null) {
                    mDataSetObserver.onChanged();
                }
            }
        }
    

    以上方法核心代码就是

    mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);

    其他可以忽略不看;这里其实用到了一种设计模式的概念,好像是叫什么装饰模式吧,意思就是把原有的 Adapter 通过HeaderViewListAdapter这个包装类重新包装一次,把改增的地方增一增,该减的地方减一点;所以我们要关注的是HeaderViewListAdapter类是如何实现的,然后再依葫芦画瓢画一个包装 RecyclerView.Adapter的包装类!

    解密HeaderViewListAdapter
    这个包装类代码本来就不长,相信大家都能够理解的,这里就挑点重点讲一下
    1、对Adapter 的 count 进行了重新计算,这个不用多解释吧!

        public int getCount() {
            if (mAdapter != null) {
                return getFootersCount() + getHeadersCount() + mAdapter.getCount();
            } else {
                return getFootersCount() + getHeadersCount();
            }
        }
    

    2、就是getView方法,这个方法是重点,如果当前 position 的位置比 HeaderView 的数量小,那么返回的就是 HeaderView 的 对应的 View,否则再判断 原 Adapter 的 count 与当前 position 的差值来比较,是调用原 Adapter 的 getView 方法,还是获取 footView 的 view;说白了这个方法的目的就是添加了头部和尾部 View。

        public View getView(int position, View convertView, ViewGroup parent) {
            // Header (negative positions will throw an IndexOutOfBoundsException)
            int numHeaders = getHeadersCount();
            if (position < numHeaders) {
                return mHeaderViewInfos.get(position).view;
            }
    
            // Adapter
            final int adjPosition = position - numHeaders;
            int adapterCount = 0;
            if (mAdapter != null) {
                adapterCount = mAdapter.getCount();
                if (adjPosition < adapterCount) {
                    return mAdapter.getView(adjPosition, convertView, parent);
                }
            }
    
            // Footer (off-limits positions will throw an IndexOutOfBoundsException)
            return mFooterViewInfos.get(adjPosition - adapterCount).view;
        }

    对于这个包装类的其他方法只要注意一下就 没问题了,那么接下来,就是我们模仿的时刻了!
    代码非常简单,这里就直接上了

    1、包装类 RecyclerWrapAdapter

    package moon.myapplication;

    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    import android.view.ViewGroup;

    import java.util.ArrayList;

    /**
    * Created by moon.zhong on 2015/7/20.
    * time : 14:10
    */
    public class RecyclerWrapAdapter extends RecyclerView.Adapter implements WrapperAdapter {

    private RecyclerView.Adapter mAdapter;
    
    private ArrayList<View> mHeaderViews;
    
    private ArrayList<View> mFootViews;
    
    static final ArrayList<View> EMPTY_INFO_LIST =
            new ArrayList<View>();
    
    private int mCurrentPosition;
    
    public RecyclerWrapAdapter(ArrayList<View> mHeaderViews, ArrayList<View> mFootViews, RecyclerView.Adapter mAdapter) {
        this.mAdapter = mAdapter;
        if (mHeaderViews == null) {
            this.mHeaderViews = EMPTY_INFO_LIST;
        } else {
            this.mHeaderViews = mHeaderViews;
        }
        if (mHeaderViews == null) {
            this.mFootViews = EMPTY_INFO_LIST;
        } else {
            this.mFootViews = mFootViews;
        }
    }
    
    public int getHeadersCount() {
        return mHeaderViews.size();
    }
    
    public int getFootersCount() {
        return mFootViews.size();
    }
    
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == RecyclerView.INVALID_TYPE) {
            return new HeaderViewHolder(mHeaderViews.get(0));
        } else if (viewType == RecyclerView.INVALID_TYPE - 1) {
            return new HeaderViewHolder(mFootViews.get(0));
        }
        return mAdapter.onCreateViewHolder(parent, viewType);
    }
    
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {
            return;
        }
        int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getItemCount();
            if (adjPosition < adapterCount) {
                mAdapter.onBindViewHolder(holder, adjPosition);
                return;
            }
        }
    }
    
    @Override
    public int getItemCount() {
        if (mAdapter != null) {
            return getHeadersCount() + getFootersCount() + mAdapter.getItemCount();
        } else {
            return getHeadersCount() + getFootersCount();
        }
    }
    
    @Override
    public int getItemViewType(int position) {
        mCurrentPosition = position;
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {
            return RecyclerView.INVALID_TYPE;
        }
        int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getItemCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getItemViewType(adjPosition);
            }
        }
        return RecyclerView.INVALID_TYPE - 1;
    }
    
    
    @Override
    public long getItemId(int position) {
        int numHeaders = getHeadersCount();
        if (mAdapter != null && position >= numHeaders) {
            int adjPosition = position - numHeaders;
            int adapterCount = mAdapter.getItemCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getItemId(adjPosition);
            }
        }
        return -1;
    }
    
    
    @Override
    public RecyclerView.Adapter getWrappedAdapter() {
        return mAdapter;
    }
    
    private static class HeaderViewHolder extends RecyclerView.ViewHolder {
        public HeaderViewHolder(View itemView) {
            super(itemView);
        }
    }
    

    }

    2、为 RecyclerView 添加 AddHeaderView 和 addFootView 方法,重写 RecyclerView

    package moon.myapplication;
    
    import android.content.Context;
    import android.support.v7.widget.RecyclerView;
    import android.util.AttributeSet;
    import android.view.View;
    
    import java.util.ArrayList;
    
    /**
     * Created by moon.zhong on 2015/7/20.
     * time : 15:14
     */
    public class WrapRecyclerView extends RecyclerView {
    
        private ArrayList<View> mHeaderViews = new ArrayList<>() ;
    
        private ArrayList<View> mFootViews = new ArrayList<>() ;
    
        private Adapter mAdapter ;
    
        public WrapRecyclerView(Context context) {
            super(context);
        }
    
        public WrapRecyclerView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public WrapRecyclerView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        public void addHeaderView(View view){
            mHeaderViews.clear();
            mHeaderViews.add(view);
            if (mAdapter != null){
                if (!(mAdapter instanceof RecyclerWrapAdapter)){
                    mAdapter = new RecyclerWrapAdapter(mHeaderViews,mFootViews,mAdapter) ;
    //                mAdapter.notifyDataSetChanged();
                }
            }
        }
    
        public void addFootView(View view){
            mFootViews.clear();
            mFootViews.add(view);
            if (mAdapter != null){
                if (!(mAdapter instanceof RecyclerWrapAdapter)){
                    mAdapter = new RecyclerWrapAdapter(mHeaderViews,mFootViews,mAdapter) ;
    //                mAdapter.notifyDataSetChanged();
                }
            }
        }
    
        @Override
        public void setAdapter(Adapter adapter) {
    
            if (mHeaderViews.isEmpty()&&mFootViews.isEmpty()){
                super.setAdapter(adapter);
            }else {
                adapter = new RecyclerWrapAdapter(mHeaderViews,mFootViews,adapter) ;
                super.setAdapter(adapter);
            }
            mAdapter = adapter ;
        }
    
    }

    到这里就实现了 RecyclerView 添加头部和尾部布局 ,用法很简单,其他步骤都不变,只需把 RecyclerView 换成WrapRecyclerView即可。

    看看效果图
    这里写图片描述

    这篇文章没有什么创新的技术点,但却给广大程序员一个很好的思路:那就是模仿,最好是能自己动手,也许你看一遍就能理解,但是真正动手实践的时候你会发现很多问题!

    源码 http://www.it165.net/uploadfile/files/2015/0723/recyclerView.zip

     

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规