美文网首页Android-RecyclerViewAndroid技术知识Android开发
如何为 RecyclerView 优雅地添加 Header、Fo

如何为 RecyclerView 优雅地添加 Header、Fo

作者: 菜鸟窝 | 来源:发表于2017-08-07 10:12 被阅读259次
    本文为菜鸟窝作者蒋志碧的连载。“从 0 开始开发一款直播 APP ”系列来聊聊时下最火的直播 APP,如何完整的实现一个类"腾讯直播"的商业化项目
    视频地址:http://www.cniao5.com/course/10121

    【从 0 开始开发一款直播 APP】3.1 高层封装之 Adapter — ListView & GridView
    【从 0 开始开发一款直播 APP】3.2 高层封装之 Adapter — RecyclerView 实现单布局展示
    【从 0 开始开发一款直播 APP】3.3 高层封装之 Adapter -- RecyclerView 实现多条目展示
    【从 0 开始开发一款直播 APP】3.4 高层封装之 Adapter -- RecyclerView 优雅的添加 Header、Footer


    一、优雅的添加 Header、Footer

    上一章讲解了 RecyclerView 封装的多条目实现聊天界面,接下来讲解 RecyclerView 实现添加 Header、Footer。运行效果如下:

    线性布局

    网格布局

    RecyclerView 系统没有封装添加头部和底部方法,但是 ListView 和 GridView 有封装,通过查看源码,可以类似封装一个添加头部和尾部的类。ListView 本身也不支持头部和尾部,是在 ListView 上做了一层包裹。

    ListView 源码解析 -- ListView.addHeaderView(View v, Object data, boolean isSelectable)

    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;
    
            //判断 Adapter 是否为空,不判断无法添加头部
            // Wrap the adapter if it wasn't already wrapped.
            if (mAdapter != null) {
                //判断是否有被包裹过
                if (!(mAdapter instanceof HeaderViewListAdapter)) {
                    wrapHeaderListAdapterInternal();
                }
                
                //观察者模式,头部添加通知
                // 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();
                }
            }
        }
    

    **ListView 源码解析 -- **HeaderViewListAdapter.java

    public class HeaderViewListAdapter implements WrapperListAdapter, Filterable {
    
        private final ListAdapter mAdapter;
    
        // These two ArrayList are assumed to NOT be null.
        // They are indeed created when declared in ListView and then shared.
        //存放头部和底部集合
        ArrayList<ListView.FixedViewInfo> mHeaderViewInfos;
        ArrayList<ListView.FixedViewInfo> mFooterViewInfos;
    
        public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos,
                                     ArrayList<ListView.FixedViewInfo> footerViewInfos,
                                     ListAdapter adapter) {
            //原始列表 Adapter
            mAdapter = adapter;
            //......
        }
        
       //移除头部
        public boolean removeHeader(View v) {
            for (int i = 0; i < mHeaderViewInfos.size(); i++) {
                ListView.FixedViewInfo info = mHeaderViewInfos.get(i);
                if (info.view == v) {
                    mHeaderViewInfos.remove(i);
    
                    mAreAllFixedViewsSelectable =
                            areAllListInfosSelectable(mHeaderViewInfos)
                            && areAllListInfosSelectable(mFooterViewInfos);
    
                    return true;
                }
            }
    
            return false;
        }
        
        //移除底部
        public boolean removeFooter(View v) {
            for (int i = 0; i < mFooterViewInfos.size(); i++) {
                ListView.FixedViewInfo info = mFooterViewInfos.get(i);
                if (info.view == v) {
                    mFooterViewInfos.remove(i);
    
                    mAreAllFixedViewsSelectable =
                            areAllListInfosSelectable(mHeaderViewInfos)
                            && areAllListInfosSelectable(mFooterViewInfos);
    
                    return true;
                }
            }
    
            return false;
        }
    
        //获取条目 
        public int getCount() {
            if (mAdapter != null) {
                //头部条目 + Adapter 条目 + 底部条目
                return getFootersCount() + getHeadersCount() + mAdapter.getCount();
            } else {
                return getFootersCount() + getHeadersCount();
            }
        }
    
        //getView 
        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;
        }
        
        //getItemViewType 和 RecyclerView 相似
        public int getItemViewType(int position) {
            int numHeaders = getHeadersCount();
            if (mAdapter != null && position >= numHeaders) {
                int adjPosition = position - numHeaders;
                int adapterCount = mAdapter.getCount();
                if (adjPosition < adapterCount) {
                    return mAdapter.getItemViewType(adjPosition);
                }
            }
    
            return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
        }
    }
    

    根据 ListView 的头部添加类,经过改写,可以得到我们自己的封装类,头部和底部可能有多个,因此需要用集合标识,这里使用 SparseArray。

    public class WrapRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
        //不包含头部和底部
        private RecyclerView.Adapter mAdapter;
    
        //由于头部和底部可能有多个,需要用标识来识别
        private int BASE_HEADER_KEY = 5500000;
        private int BASE_Footer_KEY = 6600000;
    
        //头部和底部集合 必须要用map集合进行标识 key->int  value->object
        SparseArray<View> mHeaderViews;
        SparseArray<View> mFooterViews;
    
        public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {
            mAdapter = adapter;
            mHeaderViews = new SparseArray<>();
            mFooterViews = new SparseArray<>();
        }
    
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    
            //区分头部和底部,根据ViewType来
            //ViewType可能有三部分  头部 底部 Adapter
    
            //判断是否为头部
            if (isHeaderViewType(viewType)) {
                View headerView = mHeaderViews.get(viewType);
                //header
                return createHeaderOrFooterViewHolder(headerView);
            }
            
            //判断是否为底部
            if (isFooterViewType(viewType)) {
                View footerView = mFooterViews.get(viewType);
                //footer
                return createHeaderOrFooterViewHolder(footerView);
            }
    
            //列表
            return mAdapter.onCreateViewHolder(parent, viewType);
        }
    
    
        /**
         * 创建头部和底部ViewHolder
         * @param view
         * @return
         */
        private RecyclerView.ViewHolder createHeaderOrFooterViewHolder(View view) {
            return new RecyclerView.ViewHolder(view) {};
        }
    
        @Override
        public int getItemViewType(int position) {
            //position -> viewtype  头部 底部 adapter  必须要用map集合进行标识
            //if(头部)return 头部 key
            //if(中间位置)return mAdapter.getItemViewType(position);
            //if(底部) return 底部 key
    
            //header
            if (isHeaderPosition(position)) {
                return mHeaderViews.keyAt(position);
            }
    
            // footer
            if (isFooterPosition(position)) {
                position = position - mHeaderViews.size() - mAdapter.getItemCount();
                return mFooterViews.keyAt(position);
            }
    
            //adapter
            position = position - mHeaderViews.size();
            return mAdapter.getItemViewType(position);
        }
    
    
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    
            //头部,底部不需要绑定数据
            if (isHeaderPosition(position) || isFooterPosition(position)) {
                return;
            }
    
            // Adapter
            position = position - mHeaderViews.size();
            mAdapter.onBindViewHolder(holder, position);
        }
    
        @Override
        public int getItemCount() {
            return mAdapter.getItemCount() + mHeaderViews.size() + mFooterViews.size();
        }
    
        //添加头部,底部
        public void addHeaderView(View view) {
            //没有包含头部
            int position = mHeaderViews.indexOfValue(view);
            if (position < 0) {
                //集合没有就添加,不能重复添加
                mHeaderViews.put(BASE_HEADER_KEY++, view);
            }
            notifyDataSetChanged();
        }
    
        public void addFooterView(View view) {
            //没有包含头部
            int position = mFooterViews.indexOfValue(view);
            if (position < 0) {
                //集合没有就添加,不能重复添加
                mFooterViews.put(BASE_Footer_KEY++, view);
            }
            notifyDataSetChanged();
        }
    
        //移除头部,底部
        public void removeHeaderView(View view) {
            //没有包含头部
            int index = mHeaderViews.indexOfValue(view);
            if (index < 0) return;
            //集合没有就添加,不能重复添加
            mHeaderViews.removeAt(index);
            notifyDataSetChanged();
        }
    
        public void removeFooterView(View view) {
            //没有包含底部
            int index = mFooterViews.indexOfValue(view);
            if (index < 0) return;
            //集合没有就添加,不能重复添加
            mFooterViews.removeAt(index);
            notifyDataSetChanged();
        }
    
        //是否为底部
        private boolean isFooterViewType(int viewType) {
            int footerPosition = mFooterViews.indexOfKey(viewType);
            return footerPosition >= 0;
        }
    
        //是否为头部
        private boolean isHeaderViewType(int viewType) {
            int headerPosition = mHeaderViews.indexOfKey(viewType);
            return headerPosition >= 0;
        }
    
        //是否为底部位置
        private boolean isFooterPosition(int position) {
            return position >= (mHeaderViews.size() + mAdapter.getItemCount());
        }
    
        //是否为头部位置
        private boolean isHeaderPosition(int position) {
            return position < mHeaderViews.size();
        }
    }
    

    二、线性布局添加 Header、Footer

    public class AdapterActivity extends BaseActivity {
      
        private MutipleAdaper mMutipleAdaper;
        private WrapRecyclerAdapter mWrapRecyclerAdapter;
        private RecyclerView mRecyclerView;
    
        @Override
        protected void setToolbar() {
        }
    
        @Override
        protected void setListener() {
        }
    
        @Override
        protected void initData() {
            Datas = new ArrayList<>();
            for (int i = 1; i <= 30; i++) {
                if (i % 2 == 0) {
                    Datas.add(new Item(R.drawable.tab_publish_normal,"我 get 新技能 " + i,0));
                }else {
                    Datas.add(new Item(R.drawable.tab_publish_normal,"你 get 新技能 " + i,1));
                }
            }
          
            mMutipleAdaper = new MutipleAdaper(this,Datas);
            mWrapRecyclerAdapter = new WrapRecyclerAdapter(mMutipleAdaper);
            mRecyclerView.setAdapter(mWrapRecyclerAdapter);
    
            View headerView = LayoutInflater.from(this).inflate(R.layout.layout_header,mRecyclerView,false);
            View footView = LayoutInflater.from(this).inflate(R.layout.layout_footer,mRecyclerView,false);
            mWrapRecyclerAdapter.addHeaderView(headerView);
            mWrapRecyclerAdapter.addFooterView(footView);
        }
    
        @Override
        protected void initView() {
            mRecyclerView = obtainView(R.id.recyclerView);
            mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
            mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        }
    
        @Override
        protected int getLayoutId() {
            return R.layout.activity_adapter;
        }
    }
    

    三、运行效果图

    四、WrapRecyclerView -- 自定义 RecyclerView

    可以看到上面使用头部Adapter很不友好,需要将之前封装的布局添加到包裹 Adapter 中,可以将自定义一个 RecyclerView,让其继承 RecyclerView,将 WrapRecyclerAdapter 传入,调用其方法。

    public class WrapRecyclerView extends RecyclerView {
    
        private WrapRecyclerAdapter mWrapRecyclerAdapter;
    
        private RecyclerView.Adapter mAdapter;
    
        public WrapRecyclerView(Context context) {
            this(context, null);
        }
    
        public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        @Override
        public void setAdapter(Adapter adapter) {
            //防止多次设置Adapter
            if (mAdapter != null) {
                mAdapter = null;
            }
    
            this.mAdapter = adapter;
    
            if (adapter instanceof WrapRecyclerAdapter) {
                mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter;
            } else {
                mWrapRecyclerAdapter = new WrapRecyclerAdapter(adapter);
            }
    
            //删除的问题是列表的Adapter改变,但WrapRecyclerAdapter没有改,观察者模式
            super.setAdapter(mWrapRecyclerAdapter);
    
        }
    
        //添加头部,底部
        public void addHeaderView(View view) {
            //如果没有Adapter就不添加,也可以抛异常
            //必须先设置Adapter才能添加,仿照ListView的处理方式
            if (mWrapRecyclerAdapter != null) {
                mWrapRecyclerAdapter.addHeaderView(view);
            }
        }
    
        public void addFooterView(View view) {
            if (mWrapRecyclerAdapter != null) {
                mWrapRecyclerAdapter.addFooterView(view);
            }
    
        }
    
    
        //移除头部,底部
        public void removeHeaderView(View view) {
            if (mWrapRecyclerAdapter != null) {
                mWrapRecyclerAdapter.removeHeaderView(view);
            }
        }
    
        public void removeFooterView(View view) {
            if (mWrapRecyclerAdapter != null) {
                mWrapRecyclerAdapter.removeFooterView(view);
            }
        }
    }
    

    使用的时候其它不变,只需要稍微修改

    五、WrapRecyclerView 的使用

    布局文件不能使用 RecyclerView,使用自定义的 WrapRecyclerView

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.dali.admin.fragment.AdapterActivity">
    
       <!--<android.support.v7.widget.RecyclerView-->
           <!--android:id="@+id/recyclerView"-->
           <!--android:layout_width="match_parent"-->
           <!--android:layout_height="wrap_content">-->
    
       <!--</android.support.v7.widget.RecyclerView>-->
    
       <com.dali.admin.fragment.recycler.WrapRecyclerView
           android:id="@+id/recyclerView"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"></com.dali.admin.fragment.recycler.WrapRecyclerView>
    
    </LinearLayout>
    

    在AdapterActivity 中只需要将 WrapRecyclerView 声明,直接调用 addHeaderView() 就可以添加头部,添加底部类似

        private WrapRecyclerView mRecyclerView; 
        View headerView = LayoutInflater.from(this).inflate(R.layout.layout_header,mRecyclerView,false);
        View footerView = LayoutInflater.from(this).inflate(R.layout.layout_footer,mRecyclerView,false);
    
        mRecyclerView.addHeaderView(headerView);
        mRecyclerView.addFooterView(footerView);
    

    看到这里,基本封装就完了,细心的朋友会发现没有封装监听,移除头部和底部很简单,直接添加监听就行

    headerView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mRecyclerView.removeHeaderView(v);
        }
    });
    
     footerView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mRecyclerView.removeFooterView(v);
            }
        });
    

    但是问题来了,我们在 ViewHolder 添加监听,Adapter 条目不会移除。怎么点都没反应。

    public class MutipleAdaper extends RecyclerViewAdapter<Item> {
    
        public MutipleAdaper(Context context, List<Item> datas) {
            super(context, datas, new MutipleTypeSupport<Item>() {
                @Override
                public int getLayoutId(Item item) {
                    if (item.getType() == 1){
                        return R.layout.list_item;
                    }else {
                        return R.layout.list_item1;
                    }
                }
            });
        }
    
        @Override
        protected void bindData(RecyclerViewHolder holder, final Item item, int position) {
    
            holder.setText(R.id.tv1,item.getTv1())
                    .setImageResource(R.id.img,item.getRes())
            .setOnClickListener(R.id.tv1, new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext,item.getTv1(),Toast.LENGTH_SHORT).show();
                    mDatas.remove(item);    //移除条目 item
                    notifyDataSetChanged();
                }
            });
        }
    }
    

    原因是因为我们操作的 Adapter 不是同一个,点击的条目 Adapter 是 MutipleAdaper,WrapRecyclerView 里面WrapRecyclerAdapter 没有改变,因此没有实现同步更新,这里需要使用到观察者模式。

    我们可以在 ListView 的 HeaderViewListAdapter 源码里也看到有使用观察者模式。

    public void registerDataSetObserver(DataSetObserver observer) {
        if (mAdapter != null) {
            mAdapter.registerDataSetObserver(observer);
        }
    }
    
    public void unregisterDataSetObserver(DataSetObserver observer) {
        if (mAdapter != null) {
            mAdapter.unregisterDataSetObserver(observer);
        }
    }
    

    我们也需要为其添加观察者模式,使通知到每个 Adapter,保证数据同步更新。否则列表的notifyDataSetChanged() 没效果

    public class WrapRecyclerView extends RecyclerView {
    
        private WrapRecyclerAdapter mWrapRecyclerAdapter;
        private RecyclerView.Adapter mAdapter;
    
        //重写观察者类
        private AdapterDataObserver mDataObserver = new AdapterDataObserver() {
            @Override
            public void onChanged() {
                if (mAdapter == null) return;
                if (mWrapRecyclerAdapter != mAdapter)
                    mWrapRecyclerAdapter.notifyDataSetChanged();
            }
    
            @Override
            public void onItemRangeRemoved(int positionStart, int itemCount) {
                if (mAdapter == null) return;
                if (mWrapRecyclerAdapter != mAdapter)
                    mWrapRecyclerAdapter.notifyItemRemoved(positionStart);
            }
    
            @Override
            public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
                if (mAdapter == null) return;
                if (mWrapRecyclerAdapter != mAdapter)
                    mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition);
            }
    
            @Override
            public void onItemRangeChanged(int positionStart, int itemCount) {
                if (mAdapter == null) return;
                if (mWrapRecyclerAdapter != mAdapter)
                    mWrapRecyclerAdapter.notifyItemChanged(positionStart);
            }
    
            @Override
            public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
                if (mAdapter == null) return;
                if (mWrapRecyclerAdapter != mAdapter)
                    mWrapRecyclerAdapter.notifyItemChanged(positionStart, payload);
            }
    
            @Override
            public void onItemRangeInserted(int positionStart, int itemCount) {
                if (mAdapter == null) return;
                if (mWrapRecyclerAdapter != mAdapter)
                    mWrapRecyclerAdapter.notifyItemInserted(positionStart);
            }
        };
    
    
        public WrapRecyclerView(Context context) {
            this(context, null);
        }
    
        public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        @Override
        public void setAdapter(Adapter adapter) {
    
            //防止多次设置Adapter
            if (mAdapter != null) {//注销观察者
                mAdapter.unregisterAdapterDataObserver(mDataObserver);
                mAdapter = null;
            }
    
            this.mAdapter = adapter;
    
            if (adapter instanceof WrapRecyclerAdapter) {
                mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter;
            } else {
                mWrapRecyclerAdapter = new WrapRecyclerAdapter(adapter);
            }
    
            //删除的问题是列表的 Adapter 改变,但 WrapRecyclerAdapter没有改,观察者模式
            super.setAdapter(mWrapRecyclerAdapter);
    
            //注册观察者模式
            mAdapter.registerAdapterDataObserver(mDataObserver);
    
        }
    
        //添加头部,底部
        public void addHeaderView(View view) {
    
            //如果没有Adapter就不添加,也可以抛异常
            //必须先设置Adapter才能添加,仿照ListView的处理方式
            if (mWrapRecyclerAdapter != null) {
                mWrapRecyclerAdapter.addHeaderView(view);
            }
        }
    
        public void addFooterView(View view) {
            if (mWrapRecyclerAdapter != null) {
                mWrapRecyclerAdapter.addFooterView(view);
            }
        }
    
        //移除头部,底部
        public void removeHeaderView(View view) {
    
            if (mWrapRecyclerAdapter != null) {
                mWrapRecyclerAdapter.removeHeaderView(view);
            }
        }
    
        public void removeFooterView(View view) {
            if (mWrapRecyclerAdapter != null) {
                mWrapRecyclerAdapter.removeFooterView(view);
            }
        }
    }
    

    六、完整的代码

    public class AdapterActivity extends BaseActivity {
        private MutipleAdaper mMutipleAdaper;
        private WrapRecyclerView mRecyclerView;
        private ArrayList<Item> Datas;
    
        @Override
        protected void setToolbar() {
        }
    
        @Override
        protected void setListener() {
        }
    
        @Override
        protected void initData() {
            Datas = new ArrayList<>();
            for (int i = 1; i <= 30; i++) {
                if (i % 2 == 0) {
                    Datas.add(new Item(R.drawable.tab_publish_normal,"我 get 新技能 " + i,0));
                }else {
                    Datas.add(new Item(R.drawable.tab_publish_normal,"你 get 新技能 " + i,1));
                }
            }
    
            mMutipleAdaper = new MutipleAdaper(this,Datas);
            mRecyclerView.setAdapter(mMutipleAdaper);
            View headerView = LayoutInflater.from(this).inflate(R.layout.layout_header,mRecyclerView,false);
            View footerView = LayoutInflater.from(this).inflate(R.layout.layout_footer,mRecyclerView,false);
            mRecyclerView.addHeaderView(headerView);
    
            headerView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mRecyclerView.removeHeaderView(v);
                }
            });
    
            mRecyclerView.addFooterView(footerView);
            footerView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mRecyclerView.removeFooterView(v);
                }
            });
    
        }
    
        @Override
        protected void initView() {
            mRecyclerView = obtainView(R.id.recyclerView);
            mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
            mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        }
    
        @Override
        protected int getLayoutId() {
            return R.layout.activity_adapter;
        }
    }
    

    再次使用点击事件并运行

    七、网格布局添加 Header、Footer

    可以看到基本功能已经实现,但是这里只有 LinearLayout 的布局,我们运行 GridLayout 布局,并为其添加头部和底部。只需要改写布局管理器就好

     mRecyclerView.addItemDecoration(new DividerGridItemDecoration(this));
     mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));
    

    可以看到是这种效果,并不是独占一行,为解决此问题,要在原布局添加一个方法判断是否是 GridLayoutManager,如果是,对其头部和底部显示做处理。

     public class WrapRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
         /**
         * 解决GridLayoutManager添加头部和底部不占用一行的问题
         * @param recycler
         */
        public void adjustSpanSize(RecyclerView recycler) {
            if (recycler.getLayoutManager() instanceof GridLayoutManager) {
                final GridLayoutManager layoutManager = (GridLayoutManager) recycler.getLayoutManager();
                layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                    @Override
                    public int getSpanSize(int position) {
                        boolean isHeaderOrFooter = isHeaderPosition(position) || isFooterPosition(position);
                        return isHeaderOrFooter ? layoutManager.getSpanCount() : 1;
                    }
                });
            }
        }
    }
    

    此方法要在封装的RecyclerView中调用

    public class WrapRecyclerView extends RecyclerView {
        @Override
        public void setAdapter(Adapter adapter) {
            //解决GridLayout添加头部和底部也要占据一行
            mWrapRecyclerAdapter.adjustSpanSize(this);
        }
    }
    

    再次运行

    总结

    对于 RecyclerView 的 Adapter 的封装。

    单条目的封装步骤

    1、 ViewHolder 的封装,继承 RecyclerView.ViewHolder,实现其方法和构造函数
    2、封装 ViewHolder 的 getView() 方法,避免 findViewById 及类型转换,提供设置控件和监听的一系列方法
    3、Adapter 的封装,继承 RecyclerView.Adapter 类,将封装的 ViewHolder 作为范型传入,将数据源作为范型传入。如:BaseAdapter<T> extends RecyclerView.Adapter<BaseViewHolder>。实现其构造方法和抽象方法。
    4、设置数据源,上下文对象,布局 ID,抽象绑定 ViewHolder 具体实现,将 ViewHolder 和数据源作为参数传递出去,让用户对数据进行相应处理。

    多条目的封装

    1、在单条目基础上通过构造函数传递一个提供布局的接口,通过 ViewType 类型判断到底使用什么布局。
    2、在 getItemViewType() 方法中判断布局类型,在 onCreateViewHolder() 方法中判断是否需要多布局,根据用户需要实现不同的构造函数

    头部和底部添加

    1、查看 ListView 的 addHeaderView() 及其相关源码,了解添加头部和底部的具体思路,根据其封装进行编写我们自己的方法。
    2、头部和底部考虑使用容器存储,且需要进行标识,采用 android 为我们提供的 SparseArray 进行存储,范型为 View 类型。
    3、getItemViewType() 方法根据 position 进行判断是头部、底部还是条目Adapter。
    3、onCreateViewHolder() 方法是构造 ViewHolder 的,此处应该有三类 ViewHolder,头部,底部,条目 Adapter,根据 ViewType 来进行判断是什么类型的 ViewHolder,并相应的进行构造。
    4、onBindViewHolder() 方法是绑定条目数据的,头部和底部是不需要绑定数据的,因此需要将其排除在外,在对条目 Adapter 进行绑定数据。
    5、最后添加 addHeaderView()、addFooterView()、removeHeaderView()、removeFooterView() 等方法,封装算大致完毕。
    6、对 RecyclerView 进行重写,其实就是对 RecyclerView 进行一层包裹,将封装的可添加头部和底部的 Adapter 声明,并调用其方法就行了,形成和 ListView 一样可以直接添加头部和底部的自定义控件。
    7、要实现真正的点击同步,需要使用观察者模式,实现 Adapter 操作通知同步。
    8、对于 GridView 添加头部不能独占一行进行相应处理。

    对于 RecyclerView 的封装讲到此,有不足的地方欢迎指正。

    关注菜鸟窝官网,免费领取140套开源项目

    菜鸟窝官网公号二维码.png

    加入菜鸟直播—实战app交流群

    微信图片_20170807101053.png

    相关文章

      网友评论

        本文标题:如何为 RecyclerView 优雅地添加 Header、Fo

        本文链接:https://www.haomeiwen.com/subject/kpyolxtx.html