装饰设计模式--实现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的头部,看一下整体的显示效果:
- 装饰设计模式--实现RecyclerView的addHeaderView()方法
- RecyclerView中实现addHeaderView,addFooterView功能
- 【装饰设计模式】写一个MyBufferedReader实现自己的ReadLine方法、增强BufferedReader的ReaderLine()
- 大话设计模式3 装饰模式 的java代码实现
- 漂亮的Adapter模式-体会RecyclerView的设计实现
- Java语言实现的装饰设计模式复习
- 设计模式 -- 装饰模式 -- c++实现
- 设计模式之装饰模式(C++实现)
- 设计模式实现(三)---装饰模式
- C++设计模式实现--装饰者模式
- 设计模式C++实现三:装饰模式
- 设计模式C++实现--装饰模式
- 设计模式----装饰模式(C++实现)
- 设计模式---装饰器模式(C++实现)
- 设计模式之装饰模式Java实现
- 设计模式-装饰者模式 C++实现
- 关于ListView的 addHeaderView(...) 方法
- 关于ListView的 addHeaderView(...) 方法
- java 二叉树的镜像
- java中抽象类与接口中方法访问修饰符问题
- 【iOS开发】如何给字符串添加超连接(响应事件)
- 养成好的JAVA编码习惯
- RecycleView分割线工具类
- 装饰设计模式--实现RecyclerView的addHeaderView()方法
- 如何删除github上的项目(repository)
- [Nmap渗透测试指南]第五章(伺机而动)
- 设计模式---代理模式
- Android7.0 中文API -- ZoomButton
- 快消品企业为什么要上专业的费用管理系统
- python爬虫学习roadmap
- Windows 10 使用计划任务
- bootstrap中使用modal加载kindeditor时弹出层文本框不能输入的问题