装饰设计模式--实现RecyclerView的addHeaderView()方法

来源:互联网 发布:淘宝店聚划算入口 编辑:程序博客网 时间:2024/06/06 09:40

装饰设计模式–实现RecyclerView的addHeaderView()方法

RecyclerView现在应该有越来越多的小伙伴在项目中用来替代ListView和GridView了。仅仅通过改变RecyclerView的LayoutManager就可以实现ListView和GridView以及瀑布流的效果,可谓是相当强大。但是另一方面来说,RecyclerView也体现了谷歌在做View控件时的另一种思路:实现控件的高度自主性。这具体体现在RecyclerView的自定义LayoutManager以及通过接口来让开发人员自主实现不同的分割线。本篇文章就针对ListView的addHeaderView()方法来实现RecyclerView的同样效果。

首先 分析整体的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);        // 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();            }        }    }

这就是ListView中的addHeaderView()方法的代码块了,首先传入的参数view,data等都被赋值到一个实体类中并mHeaderViewInfos.add(info)进行存储。存储结束之后大家注意一下这个判断语句:

    if (!(mAdapter instanceof HeaderViewListAdapter)) {                mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);            }

如果这个mAdapte一个r不是HeaderViewListAdapter,那么就把它转化成一个HeaderViewListAdapter,同时把这个原本的mAdapter通过构造方法传递到HeaderViewListAdapter这个类中,是不是就是一个替换啊!对吧?
那么这个mAdapter是什么呢?来看一下ListView的setAdapter()方法:

  public void setAdapter(ListAdapter adapter) {        if (mAdapter != null && mDataSetObserver != null) {            mAdapter.unregisterDataSetObserver(mDataSetObserver);        }        resetList();        mRecycler.clear();        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {            mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);        } else {            mAdapter = adapter;        }        mOldSelectedPosition = INVALID_POSITION;        mOldSelectedRowId = INVALID_ROW_ID;        // AbsListView#setAdapter will update choice mode states.        super.setAdapter(adapter);

这个方法有点长我就只复制了需要分析的一部分啊,具体完整的有兴趣的朋友可以去看看。
这个方法里边中间的判断语句是不是有点类似上面的?大家还记得刚刚说过的,你在调用ListView的addHeaderView()方法时,它内部就是用这个mHeaderViewInfos集合来进行数据存储的,对吧?所以,我们平常在给ListView.setAdapter()时,它内部会先去判断是否你前面add过headerView,以此来判断应该给你转化为一个HeaderViewListAdapter还是直接给你 mAdapter = adapter。对吧,所以咱们前面的疑问,这个mAdapter是什么?是不是有答案了?就是你代码里面传入的自定义的adapter,对吧。
通过这么一大段分析,大家应该有点明白一点了,我们在给ListView添加头部或者底部的View时,它内部会直接创建一个HeaderViewListAdapter来代替掉你传入的adapter,并且将这个你传入的adapter通过构造方法,传给HeaderViewListAdapter。OK,ListView这边整理完毕咱们就开始看HeaderViewListAdapter的代码。
看源码大家一定要带着疑问去看,带着目的去找,不然会很乱,理不出思路。这边咱们就慢慢的一个问题一个问题的来解决。首先一个重要的问题是:咱们set进去的adapter,通过构造方法传到了HeaderViewListAdapter这边,它为什么传过来?有什么用?
这个类的整体代码不长,大家看一下应该可以看懂的,不难,我这边随便截取了一些方法代码:

 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;    }    public int getViewTypeCount() {        if (mAdapter != null) {            return mAdapter.getViewTypeCount();        }        return 1;    }    public void registerDataSetObserver(DataSetObserver observer) {        if (mAdapter != null) {            mAdapter.registerDataSetObserver(observer);        }    }

代码写的很清楚啊,这些方法最后,是不是都是用的咱们在ListView中set的mAdapter来调用相同的方法。什么意思?意思就是说在这个类中,先是拿代码处理了一些事儿,最后又用这个mAdapter去调用应该调用的adapter的方法了,对吧。
这样的设计模式咱们就可以直接作用在RecyclerView中,咱们是不是也可以写一个HeaderViewListAdapter来先处理一下headerView再让他重新回到adapter的道路上来调方法呢?这样就很清楚了。
OK,这就可以开始咱们的copy源码之路了
第一步,自定义一个MyRecyclerView,重写setAdapter和添加addHeaderView()方法:

public class MyRecyclerView extends RecyclerView {    private ArrayList<View> mHeaderViewInfos=new ArrayList<>();    private ArrayList<View> mFooterViewInfos=new ArrayList<>();    private Adapter mAdapter;    public MyRecyclerView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);    }    public void addHeaderView(View v) {        mHeaderViewInfos.add(v);        // Wrap the adapter if it wasn't already wrapped.        if (mAdapter != null) {            if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) {                mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);            }        }    }    public void addFooterView(View v) {        mFooterViewInfos.add(v);        // Wrap the adapter if it wasn't already wrapped.        if (mAdapter != null) {            if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) {                mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);            }        }    }    @Override    public void setAdapter(Adapter adapter) {        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {            mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);        } else {            mAdapter = adapter;        }        super.setAdapter(mAdapter);    }}

这就是整个MyRecyclerView的代码了,当然了因为是例子显示,所以就简化了一些,比如addHeaderView仅仅需要传入大一个view即可。大家如果和我一样copy源码,有一个地方需要注意,就是最后super.setAdapter(mAdapter)这里不是和ListView一样super.setAdapter(adapter),具体原因大家应该都懂吧,我这就不说了。
接下来处理咱们自定义的adapter了:

public class HeaderViewRecyclerAdapter extends RecyclerView.Adapter {    public static final int SHOW_HEADER=1;    public static final int SHOW_FOOTER=2;    private ArrayList<View> mHeaderViewInfos;    private ArrayList<View> mFooterViewInfos;    private RecyclerView.Adapter mAdapter;    public HeaderViewRecyclerAdapter(ArrayList<View> haderViewInfos, ArrayList<View> footerViewInfos, RecyclerView.Adapter mAdapter) {        if (haderViewInfos == null) {            mHeaderViewInfos = new ArrayList<View>();        } else {            mHeaderViewInfos = haderViewInfos;        }        if (footerViewInfos == null) {            mFooterViewInfos = new ArrayList<View>();        } else {            mFooterViewInfos = footerViewInfos;        }       this.mAdapter=mAdapter;    }

HeaderViewRecyclerAdapter类的构造方法,就是判断如果为null就新建一个集合,so easy。再接下来写一下简单的方法:

 @Override    public int getItemCount() {        if (mAdapter != null) {            return getFootersCount() + getHeadersCount() + mAdapter.getItemCount();        } else {            return getFootersCount() + getHeadersCount();        }    }    public int getHeadersCount() {        return mHeaderViewInfos.size();    }    public int getFootersCount() {        return mFooterViewInfos.size();    }

这里就是copy加修改了,也没什么好说的。
接下来就是重点了,ListView的界面显示大家应该都清楚,是在getView()方法中,没错吧,那么RecyclerView呢?它没有这个方法啊。
所以这里是不是需要重点分析一下?大家先看看ListView捆绑的HeaderViewListAdapter中的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;    }

这一块的逻辑是不是比较简单啊。首先获取到你存储头部view的集合的size,然后拿来和position作对比,如果有头部view,就返回这个头部的view。中间部分代码是不是又重新将position转化为adjPosition,重新转成了0·1·2·3···这些position了对吧。然后在return中调用了mAdapter.getView(adjPosition, convertView, parent),中间部分就是处理的listview的每一项item的view,没错吧。最后就是底部的view了。这个方法是不是就是通过对比position和头部view的集合来返回各自的view?那么recyclerView中我们该怎么区分position的不同类型呢?
大家在使用RecyclerView时自定义继承的RecyclerView.Adapter,是不是重要的和view挂钩的方法就下面这两个:

  @Override    @Override    public int getItemViewType(int position) {        return super.getItemViewType(position);    }    @Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        return null;    }

稍微了解RecyclerView的都知道,真正的View的处理是在onCreateViewHolder()方法中,但是我们不可能在这个方法中分析view的类型,因为他没有position让我们进行对比。但是onCreateViewHolder()中有一个viewType的参数,那么我们是不是就可以将这个viewType进行区分呢?

   /**         * Return the view type of the item at <code>position</code> for the purposes         * of view recycling.         *         * <p>The default implementation of this method returns 0, making the assumption of         * a single view type for the adapter. Unlike ListView adapters, types need not         * be contiguous. Consider using id resources to uniquely identify item view types.         *         * @param position position to query         * @return integer value identifying the type of the view needed to represent the item at         *                 <code>position</code>. Type codes need not be contiguous.         */        public int getItemViewType(int position) {            return 0;        }

getItemViewType()源码是不是已经告诉大家了,这个方法返回值就是view的type,没错吧,所以直接开始搞,在getItemViewType()方法中我们去return不同的viewType,然后再onCreateViewHolder()中区分viewType并进行return不同的view。

 public static final int SHOW_HEADER=1; public static final int SHOW_FOOTER=2; @Override    public int getItemViewType(int position) {        //header        int numHeaders = getHeadersCount();        if (position < numHeaders) {            return SHOW_HEADER;        }        // Adapter        final int adjPosition = position - numHeaders;        int adapterCount = 0;        if (mAdapter != null) {            adapterCount = mAdapter.getItemCount();            if (adjPosition < adapterCount) {                return mAdapter.getItemViewType(adjPosition);            }        }        // Footer (off-limits positions will throw an IndexOutOfBoundsException)        return SHOW_FOOTER;    }    @Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        if(viewType==SHOW_HEADER){            return new HeaderViewHolder(mHeaderViewInfos.get(0));        }        if(viewType==SHOW_FOOTER){            return new HeaderViewHolder(mHeaderViewInfos.get(0));        }        return mAdapter.onCreateViewHolder(parent,viewType);    }

接下来粘贴的是HeaderViewRecyclerAdapter完整源码:

public class HeaderViewRecyclerAdapter extends RecyclerView.Adapter {    public static final int SHOW_HEADER=1;    public static final int SHOW_FOOTER=2;    private ArrayList<View> mHeaderViewInfos;    private ArrayList<View> mFooterViewInfos;    private RecyclerView.Adapter mAdapter;    public HeaderViewRecyclerAdapter(ArrayList<View> haderViewInfos, ArrayList<View> footerViewInfos, RecyclerView.Adapter mAdapter) {        if (haderViewInfos == null) {            mHeaderViewInfos = new ArrayList<View>();        } else {            mHeaderViewInfos = haderViewInfos;        }        if (footerViewInfos == null) {            mFooterViewInfos = new ArrayList<View>();        } else {            mFooterViewInfos = footerViewInfos;        }       this.mAdapter=mAdapter;    }    @Override    public int getItemViewType(int position) {        //header        int numHeaders = getHeadersCount();        if (position < numHeaders) {            return SHOW_HEADER;        }        // Adapter        final int adjPosition = position - numHeaders;        int adapterCount = 0;        if (mAdapter != null) {            adapterCount = mAdapter.getItemCount();            if (adjPosition < adapterCount) {                return mAdapter.getItemViewType(adjPosition);            }        }        // Footer (off-limits positions will throw an IndexOutOfBoundsException)        return SHOW_FOOTER;    }    @Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        if(viewType==SHOW_HEADER){            return new HeaderViewHolder(mHeaderViewInfos.get(0));        }        if(viewType==SHOW_FOOTER){            return new HeaderViewHolder(mFooterViewInfos.get(0));        }        return mAdapter.onCreateViewHolder(parent,viewType);    }    @Override    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {        //也要划分三个区域        int numHeaders = getHeadersCount();        if (position < numHeaders) {//是头部            return;        }        //adapter body        final int adjPosition = position - numHeaders;        int adapterCount = 0;        if (mAdapter != null) {            adapterCount = mAdapter.getItemCount();            if (adjPosition < adapterCount) {                mAdapter.onBindViewHolder(holder, adjPosition);                return;            }        }        //footer    }    @Override    public int getItemCount() {        if (mAdapter != null) {            return getFootersCount() + getHeadersCount() + mAdapter.getItemCount();        } else {            return getFootersCount() + getHeadersCount();        }    }    public int getHeadersCount() {        return mHeaderViewInfos.size();    }    public int getFootersCount() {        return mFooterViewInfos.size();    }    class HeaderViewHolder extends  RecyclerView.ViewHolder{        public HeaderViewHolder(View itemView) {            super(itemView);        }    }}

MainActivity中的代码:

public class MainActivity extends AppCompatActivity {    private MyRecyclerView recylerview;    private ArrayList<String> list;    private MyRecyclerAdapter adapter;//  private MyStaggedRecyclerAdapter adapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        list = new ArrayList<String>();        for (int i = 0; i < 60; i++) {            list.add("item"+i);        }        recylerview = (MyRecyclerView)findViewById(R.id.recyclerView);        adapter = new MyRecyclerAdapter(list);        recylerview.setLayoutManager( new LinearLayoutManager(this));//默认垂直        TextView textView=new TextView(this);        textView.setText("head");        recylerview.addHeaderView(textView);        recylerview.setAdapter(adapter);        recylerview.setItemAnimator(new DefaultItemAnimator());        adapter.setOnItemClickListener(new MyRecyclerAdapter.OnItemClickListener() {            @Override            public void onItemClick(View view, int position) {                Toast.makeText(MainActivity.this, "点我干嘛"+position,Toast.LENGTH_SHORT).show();            }        });        ListView listView;    }}

我这里直接new了一个TextView添加在了RecyclerView的头部,看一下整体的显示效果:

这里写图片描述

阅读全文
1 0