真实项目运用-RecyclerView封装
Posted CameloeAnthony
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了真实项目运用-RecyclerView封装相关的知识,希望对你有一定的参考价值。
原文链接:从零开始搭建android框架系列
项目地址:MVPCommon
前言
很久没有发表从零开始搭建android框架系列这个系列的文章了 。由于最近工作确实有点忙碌,也在脚踏实地的花时间研究android方面自己很多不懂的东西。但是写博客确实是一个坚持不懈和自我提高的过程,也希望在保持文章更新的同时能够保持文章的质量 。之前翻译了一些文章,有兴趣的小伙伴可以去看一下。今天这篇文章来谈一谈RecyclerView的封装,对RecyclerView的一些使用点进行总结,以及如何将RecyclerView的adapter进一步简化。平时开发使用的RecyclerView Adapter是来自鸿洋大神的为RecyclerView打造通用Adapter 让RecyclerView更加好用以及对应的github项目baseAdapter github.但是有个问题是他这篇文章写的时间比较早,项目一直在维护,所以本篇文章也算是对整个项目的思路的再梳理。
刚好解决了昨天在鸿洋博客下看到的这个小问题。哈哈。希望对大家有帮助。
项目结构
首先看看我的项目结构,项目分为common 和module模块,这里对之前整个项目的框架进行了改造,没有了之前的library,取而代之的是将所有公用组件放在了common包中,这是每个项目都可以copy使用的部分。在module包中就是具体每个项目的每个模块。比如这个示例项目中,包含
recyclerView组件作为每个项目中都可以使用的组件,这里放在common-widgets-recyclerview包下。
这里可以看到的recyclerView组件这里添加了adapter,base,divider,section,utils,wrapper包。下面来进行深入的讲解以及怎样在项目开发中进行使用吧。
RecyclerView基础
RecyclerView is a more advanced and flexible version of ListView. This widget is a container for large sets of views that can be recycled and scrolled very efficiently. Use the RecyclerView widget when you have lists with elements that change dynamically.
RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好。RecyclerView与ListView原理是类似的:都是仅仅维护少量的View并且可以展示大量的数据集。在RecyclerView标准化了ViewHolder类似于ListView中convertView用来做视图缓存。
请直接参考 Android RecyclerView 使用完全解析 体验艺术般的控件
ViewHolder
ViewHolder是google在优化ListView性能的技巧上就提到的,虽然google并没有强制使用,但事实上它已经成为ListView的编写规范。在RecyclerView上,ViewHolder的使用成为了一种强制手段了。接下来对封装的ViewHolder进行分析:
首先来看看ViewHolder的用法,这是一个简单的获取String数组并展现到TextView上。通过数组的大小来创建item的数量。
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>
public String[] datas = null;
public MyAdapter(String[] datas)
this.datas = datas;
//创建新View,被LayoutManager所调用
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType)
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item,viewGroup,false);
ViewHolder vh = new ViewHolder(view);
return vh;
//将数据与界面进行绑定的操作
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position)
viewHolder.mTextView.setText(datas[position]);
//获取数据的数量
@Override
public int getItemCount()
return datas.length;
//自定义的ViewHolder,持有每个Item的的所有界面元素
public static class ViewHolder extends RecyclerView.ViewHolder
public TextView mTextView;
public ViewHolder(View view)
super(view);
mTextView = (TextView) view.findViewById(R.id.text);
当然这里只是简单的一个TextView,但是当数据多起来之后,很多TextView,ImageView,以及代码段
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item,viewGroup,false);
ViewHolder vh = new ViewHolder(view);
都可以进行稍微修改。现在ViewHolder修改如下:
public class ViewHolder extends RecyclerView.ViewHolder
private SparseArray<View> mViews;
private View mConvertView;
private Context mContext;
ImageLoaderUtil imageLoaderUtil;
public ViewHolder(Context context, View itemView)
super(itemView);
mContext = context;
mConvertView = itemView;
mViews = new SparseArray<>();
imageLoaderUtil = new ImageLoaderUtil();
public static ViewHolder createViewHolder(Context context, View itemView)
return new ViewHolder(context, itemView);
public static ViewHolder createViewHolder(Context context,
ViewGroup parent, int layoutId)
View itemView = LayoutInflater.from(context).inflate(layoutId, parent,
false);
return new ViewHolder(context, itemView);
/**
* 通过viewId获取控件
*
* @param viewId
* @return
*/
public <T extends View> T getView(int viewId)
View view = mViews.get(viewId);
if (view == null)
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
return (T) view;
public View getConvertView()
return mConvertView;
/****以下为辅助方法*****/
/**
* 设置TextView的值
*
* @param viewId
* @param text
* @return
*/
public ViewHolder setText(int viewId, String text)
TextView tv = getView(viewId);
tv.setText(text);
return this;
public ViewHolder setImageResource(int viewId, int resId)
ImageView view = getView(viewId);
view.setImageResource(resId);
return this;
public ViewHolder setImageUrl(int viewId, String url)
ImageView view = getView(viewId);
ImageLoader.Builder builder = new ImageLoader.Builder();
ImageLoader img = builder.url(url)
.imgView(view).strategy(ImageLoaderUtil.LOAD_STRATEGY_ONLY_WIFI).build();
imageLoaderUtil.loadImage(mContext, img);
return this;
public ViewHolder setImageBitmap(int viewId, Bitmap bitmap)
ImageView view = getView(viewId);
view.setImageBitmap(bitmap);
return this;
public ViewHolder setImageDrawable(int viewId, Drawable drawable)
ImageView view = getView(viewId);
view.setImageDrawable(drawable);
return this;
......
......
/**
* 关于事件的
*/
public ViewHolder setOnClickListener(int viewId,
View.OnClickListener listener)
View view = getView(viewId);
view.setOnClickListener(listener);
return this;
......
这里需要关注的是getView方法,直接返回当前view的类型。
所以我们可以在使用viewholder的时候
holder.setText(R.id.text_view, "text");
就完成了textView的setText操作。而没有进行类型转换。当然这里省去了findViewById的步骤的同时,使用private SparseArray<View> mViews
进行所有view的保存,也就是在牺牲一定内存性能的情况下,确保了代码的整洁性。还需要关注上面的
public ViewHolder setImageUrl(int viewId, String url)
ImageView view = getView(viewId);
ImageLoader.Builder builder = new ImageLoader.Builder();
ImageLoader img = builder.url(url)
.imgView(view).strategy(ImageLoaderUtil.LOAD_STRATEGY_ONLY_WIFI).build();
imageLoaderUtil.loadImage(mContext, img);
return this;
这里只需要传入ImageView的id,和url就可以解析网络图片并完成加载。使用的是Glide进行图片加载。具体可以参考之前的文章网络图片加载的封装.这样封装还有一个好处是当你遇到奇葩的服务器返回字段,比如说我们前段时间遇到的每次返回的textView的text都是有问题的,需要我们自己处理,比如说时间需要截取并返回刚刚,几小时前,我们都可以在ViewHolder进行统一的处理,而不需要在每个数据获取的时候进行处理。
多Item布局实现
这也是我们使用RecyclerView和ListView中过程中经常遇到的问题。看看网易新闻的列表样式,顶部大图,标题+三张图片,标题+左侧图片,视频样式,广告样式……. 这种情况我们怎么便捷快速处理呢?
看看通常处理itemView的type类型不同的方法:
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
class ViewHolder0 extends RecyclerView.ViewHolder
...
class ViewHolder2 extends RecyclerView.ViewHolder
...
@Override
public int getItemViewType(int position)
// Just as an example, return 0 or 2 depending on position
// Note that unlike in ListView adapters, types don't have to be contiguous
return position % 2 * 2;
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
switch (viewType)
case 0: return new ViewHolder0(...);
case 2: return new ViewHolder2(...);
...
这里对方法getItemViewType()进行重写, 并且在 onCreateViewHolder()针对不同的viewType进行不同的ViewHolder创建。
同时,这样使用不同的type 来处理不同的位置的数据,也能解决ListView中经常遇到的一个问题,那就是header和footer 的view的添加。我们只需要针对首尾位置进行itemViewType 的处理并且返回header和footer的view就行了。
这里也对这种情况进行了处理的封装:
看看实际项目中的效果,一个adapter就完成了所有的不同的item类型操作。
整个类继承自MultiItemTypeAdapter<T>
:
/**
* Created by Anthony
* <p/>
*
*/
public class NewsMultiAdapter extends MultiItemTypeAdapter<NewsItem>
public NewsMultiAdapter(Context context)
super(context);
addItemViewDelegate(new TodayTopicDelegate());// docType = 5, 今日头条样式
addItemViewDelegate(new JustTitleDelegate());// docType = 4, 纯文字样式
addItemViewDelegate(new OtherTypeDelegate());// docType = 0/1, 默认左侧图片 + 右侧标题,描述字段样式
// addItemViewDelegate(new BigPicTypeDelegate()); //docType = 2, 顶部标题 + 一张大横图样式
/*
docType = 5, 今日头条样式
docType = 0, 默认左侧图片 + 右侧标题,描述字段样式
docType = 1, 顶部标题 + 三张图片样式
docType = 2, 顶部标题 + 一张大横图样式
docType = 3, 默认样式 + 图集图标 -->点击进入图集细览详情
docType = 4, 纯文字样式
docType = 5, 今日头条样式
docType = 6, 专题样式
*/
public class TodayTopicDelegate implements ItemViewDelegate<NewsItem>
@Override
public int getItemViewLayoutId()
return R.layout.gz_tab1_item_today_topic;
@Override
public boolean isForViewType(NewsItem item, int position)
return item.getType() == 5;
@Override
public void convert(ViewHolder holder, NewsItem item, int position)
holder.setText(R.id.tv_title_center, item.getTitle());
holder.setText(R.id.tv_news_date, item.getTime());
public class OtherTypeDelegate implements ItemViewDelegate<NewsItem>
@Override
public int getItemViewLayoutId()
return R.layout.gz_tab1_item_normal_news;
@Override
public boolean isForViewType(NewsItem item, int position)
return item.getType() == 0;
@Override
public void convert(ViewHolder holder, NewsItem item, int position)
holder.setText(R.id.tv_title_center, item.getTitle());
holder.setText(R.id.tv_news_source, item.getSummary());
holder.setText(R.id.tv_news_date, item.getTime());
if (item.getImgs() != null)
String url = item.getImgs().get(0);
holder.setImageUrlInGZ(R.id.img_news_image, url);
public class JustTitleDelegate implements ItemViewDelegate<NewsItem>
@Override
public int getItemViewLayoutId()
return R.layout.gz_tab1_item_just_title;
@Override
public boolean isForViewType(NewsItem item, int position)
return item.getType() == 4;
@Override
public void convert(ViewHolder holder, NewsItem item, int position)
holder.setText(R.id.tv_title_center, item.getTitle());
holder.setText(R.id.tv_news_date, item.getDate());
来看看MultiItemTypeAdapter
/**
* Created by zhy on 16/4/9.
*/
public class MultiItemTypeAdapter<T> extends RecyclerView.Adapter<ViewHolder>
protected Context mContext;
protected List<T> mDatas;
protected ItemViewDelegateManager mItemViewDelegateManager;
protected OnItemClickListener<T> mOnItemClickListener;
public int offset = 0;
public MultiItemTypeAdapter(Context context, List<T> datas)
mContext = context;
mDatas = datas;
mItemViewDelegateManager = new ItemViewDelegateManager();
public MultiItemTypeAdapter(Context context)
this(context, new ArrayList<T>());
@Override
public int getItemViewType(int position)
if (!useItemViewDelegateManager()) return super.getItemViewType(position);
return mItemViewDelegateManager.getItemViewType(mDatas.get(position), position);
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
int layoutId = mItemViewDelegateManager.getItemViewLayoutId(viewType);
ViewHolder holder = ViewHolder.createViewHolder(mContext, parent, layoutId);
setListener(parent, holder, viewType);
return holder;
public void convert(ViewHolder holder, T t)
mItemViewDelegateManager.convert(holder, t, holder.getAdapterPosition());
protected boolean isEnabled(int viewType)
return true;
protected void setListener(final ViewGroup parent, final ViewHolder viewHolder, int viewType)
if (!isEnabled(viewType)) return;
viewHolder.getConvertView().setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
if (mOnItemClickListener != null)
int position = viewHolder.getAdapterPosition();
mOnItemClickListener.onItemClick(v, viewHolder, mDatas.get(position - offset), position);
);
viewHolder.getConvertView().setOnLongClickListener(new View.OnLongClickListener()
@Override
public boolean onLongClick(View v)
if (mOnItemClickListener != null)
int position = viewHolder.getAdapterPosition();
return mOnItemClickListener.onItemLongClick(v, viewHolder, mDatas.get(position - offset), position);
return false;
);
@Override
public void onBindViewHolder(ViewHolder holder, int position)
convert(holder, mDatas.get(position));
@Override
public int getItemCount()
int itemCount = mDatas.size();
return itemCount;
public List<T> getDatas()
return mDatas;
public MultiItemTypeAdapter addItemViewDelegate(ItemViewDelegate<T> itemViewDelegate)
mItemViewDelegateManager.addDelegate(itemViewDelegate);
return this;
public MultiItemTypeAdapter addItemViewDelegate(int viewType, ItemViewDelegate<T> itemViewDelegate)
mItemViewDelegateManager.addDelegate(viewType, itemViewDelegate);
return this;
protected boolean useItemViewDelegateManager()
return mItemViewDelegateManager.getItemViewDelegateCount() > 0;
public interface OnItemClickListener<T>
void onItemClick(View view, RecyclerView.ViewHolder holder, T o, int position);
boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, T o, int position);
public void setOnItemClickListener(OnItemClickListener onItemClickListener)
this.mOnItemClickListener = onItemClickListener;
public void addDataAll(List data)
mDatas.addAll(data);
public void clearData()
mDatas.clear();
MultiItemTypeAdapter<T>
,这里主要完成了List形式添加数据,数据类型使用泛型操作,只需要在构造函数中,或者public方法 addDataAll
就可以添加列表类型数据。利用ItemViewDelegateManager
完成不同类型type的管理.而添加不同的type是对接口ItemViewDelegate
的实现。ItemViewDelegateManager
起到了一个中间管理者和代理者的作用。具体看下面的代码:
/**
* Created by zhy on 16/6/22.
*/
public interface ItemViewDelegate<T>
int getItemViewLayoutId();
boolean isForViewType(T item, int position);
void convert(ViewHolder holder, T t, int position);
package com.app.gzgov.common.widgets.recyclerview.base;
import android.support.v4.util.SparseArrayCompat;
/**
* Created by zhy on 16/6/22.
*/
public class ItemViewDelegateManager<T>
SparseArrayCompat<ItemViewDelegate<T>> delegates = new SparseArrayCompat();
public int getItemViewDelegateCount()
return delegates.size();
public ItemViewDelegateManager<T> addDelegate(ItemViewDelegate<T> delegate)
int viewType = delegates.size();
if (delegate != null)
delegates.put(viewType, delegate);
viewType++;
return this;
public ItemViewDelegateManager<T> addDelegate(int viewType, ItemViewDelegate<T> delegate)
if (delegates.get(viewType) != null)
throw new IllegalArgumentException(
"An ItemViewDelegate is already registered for the viewType = "
+ viewType
+ ". Already registered ItemViewDelegate is "
+ delegates.get(viewType));
delegates.put(viewType, delegate);
return this;
public ItemViewDelegateManager<T> removeDelegate(ItemViewDelegate<T> delegate)
if (delegate == null)
throw new NullPointerException("ItemViewDelegate is null");
int indexToRemove = delegates.indexOfValue(delegate);
if (indexToRemove >= 0)
delegates.removeAt(indexToRemove);
return this;
public ItemViewDelegateManager<T> removeDelegate(int itemType)
int indexToRemove = delegates.indexOfKey(itemType);
if (indexToRemove >= 0)
delegates.removeAt(indexToRemove);
return this;
public int getItemViewType(T item, int position)
int delegatesCount = delegates.size();
for (int i = delegatesCount - 1; i >= 0; i--)
ItemViewDelegate<T> delegate = delegates.valueAt(i);
if (delegate.isForViewType( item, position))
return delegates.keyAt(i);
throw new IllegalArgumentException(
"No ItemViewDelegate added that matches position=" + position + " in data source");
public void convert(ViewHolder holder, T item, int position)
int delegatesCount = delegates.size();
for (int i = 0; i < delegatesCount; i++)
ItemViewDelegate<T> delegate = delegates.valueAt(i);
if (delegate.isForViewType( item, position))
delegate.convert(holder, item, position);
return;
throw new IllegalArgumentException(
"No ItemViewDelegateManager added that matches position=" + position + " in data source");
public int getItemViewLayoutId(int viewType)
return delegates.get(viewType).getItemViewLayoutId();
public int getItemViewType(ItemViewDelegate itemViewDelegate)
return delegates.indexOfValue(itemViewDelegate);
这里也就解决了多种itemViewType的问题。实现了方便的添加不同的类型的item数据。泛型数据降低了代码的耦合度。
一种item布局的实现:
这里提供一种item布局,就只需要对MultiItemTypeAdapter进行限定一种layout类型。并且isForViewType方法返回true,代表着始终返回当前的layout。
那么对于只有一种类型的列表数据
/**
* Created by zhy on 16/4/9.
*/
public abstract class CommonAdapter<T> extends MultiItemTypeAdapter<T>
protected Context mContext;
protected int mLayoutId;
protected List<T> mDatas;
protected LayoutInflater mInflater;
public CommonAdapter(final Context context, final int layoutId)
this(context, layoutId, new ArrayList<T>());
public CommonAdapter(final Context context, final int layoutId, List<T> datas)
super(context, datas);
mContext = context;
mInflater = LayoutInflater.from(context);
mLayoutId = layoutId;
mDatas = datas;
addItemViewDelegate(new ItemViewDelegate<T>()
@Override
public int getItemViewLayoutId()
return layoutId;
@Override
public boolean isForViewType(T item, int position)
return true;
@Override
public void convert(ViewHolder holder, T t, int position)
CommonAdapter.this.convert(holder, t, position);
);
protected abstract void convert(ViewHolder holder, T t, int position);
具体的新闻类型:
public class NewsSingleAdapter extends CommonAdapter<NewsItem>
public NewsSingleAdapter(Context context)
super(context, R.layout.prj_list_item_news);
@Override
protected void convert(ViewHolder holder, NewsItem item, int position)
holder.setText(R.id.tv_news_title, item.getTitle());
holder.setText(R.id.tv_news_summary, item.getSummary());
holder.setText(R.id.tv_news_date, item.getTime());
holder.setImageUrl(R.id.img_news_image,item.getImgs().get(0));
这里也就实现了单一的列表形式,比如网易新闻的专题样式:
加载更多以及header 和footer的添加
这里直接参加wrapper包中几个类,
这里是对不同的item的type 类型进行控制,从而得到了不同的RecyclerView的样式。具体可以参考我的MVPCommon中的代码。
/**
* Created by zhy on 16/6/23.
*/
public class HeaderAndFooterWrapper<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder>
private static final int BASE_ITEM_TYPE_HEADER = 100000;
private static final int BASE_ITEM_TYPE_FOOTER = 200000;
private SparseArrayCompat<View> mHeaderViews = new SparseArrayCompat<>();
private SparseArrayCompat<View> mFootViews = new SparseArrayCompat<>();
private RecyclerView.Adapter mInnerAdapter;
private RecyclerView.Adapter mNotifyAdapter;
public HeaderAndFooterWrapper(RecyclerView.Adapter adapter)
mInnerAdapter = adapter;
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
if (mHeaderViews.get(viewType) != null)
ViewHolder holder = ViewHolder.createViewHolder(parent.getContext(), mHeaderViews.get(viewType));
return holder;
else if (mFootViews.get(viewType) != null)
ViewHolder holder = ViewHolder.createViewHolder(parent.getContext(), mFootViews.get(viewType));
return holder;
return mInnerAdapter.onCreateViewHolder(parent, viewType);
@Override
public int getItemViewType(int position)
if (isHeaderViewPos(position))
return mHeaderViews.keyAt(position);
else if (isFooterViewPos(position))
return mFootViews.keyAt(position - getHeadersCount() - getRealItemCount());
return mInnerAdapter.getItemViewType(position - getHeadersCount());
private int getRealItemCount()
return mInnerAdapter.getItemCount();
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
if (isHeaderViewPos(position))
return;
if (isFooterViewPos(position))
return;
mInnerAdapter.onBindViewHolder(holder, position - getHeadersCount());
@Override
public int getItemCount()
return getHeadersCount() + getFootersCount() + getRealItemCount();
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView)
mNotifyAdapter = recyclerView.getAdapter();
WrapperUtils.onAttachedToRecyclerView(mInnerAdapter, recyclerView, new WrapperUtils.SpanSizeCallback()
@Override
public int getSpanSize(GridLayoutManager layoutManager, GridLayoutManager.SpanSizeLookup oldLookup, int position)
int viewType = getItemViewType(position);
if (mHeaderViews.get(viewType) != null)
return layoutManager.getSpanCount();
else if (mFootViews.get(viewType) != null)
return layoutManager.getSpanCount();
if (oldLookup != null)
return oldLookup.getSpanSize(position);
return 1;
);
@Override
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder)
mInnerAdapter.onViewAttachedToWindow(holder);
int position = holder.getLayoutPosition();
if (isHeaderViewPos(position) || isFooterViewPos(position))
WrapperUtils.setFullSpan(holder);
private boolean isHeaderViewPos(int position)
return position < getHeadersCount();
private boolean isFooterViewPos(int position)
return position >= getHeadersCount() + getRealItemCount();
public void addHeaderView(View view)
int key = findHeaderKeyByView(view);
if (key == -1)
mHeaderViews.put(mHeaderViews.size() + BASE_ITEM_TYPE_HEADER, view);
if (mNotifyAdapter != null)
mNotifyAdapter.notifyDataSetChanged();
if (mInnerAdapter instanceof MultiItemTypeAdapter)
((MultiItemTypeAdapter) mInnerAdapter).offset += 1;
public void addFootView(View view)
mFootViews.put(mFootViews.size() + BASE_ITEM_TYPE_FOOTER, view);
public int getHeadersCount()
return mHeaderViews.size();
public int getFootersCount()
return mFootViews.size();
public void deleteHeaderView(View view)
// if (mHeaderViews.size() > position && position >=0 )
// View v = mHeaderViews.get(position + BASE_ITEM_TYPE_HEADER, null);
// if (v != null)
// mHeaderViews.remove(position + BASE_ITEM_TYPE_HEADER);
// if (mInnerAdapter instanceof MultiItemTypeAdapter)
// ((MultiItemTypeAdapter) mInnerAdapter).offset -= 1;
//
// if (mNotifyAdapter != null)
// mNotifyAdapter.notifyDataSetChanged();
//
//
// for(int i=0; i<mHeaderViews.size(); i++)
// int key = mHeaderViews.keyAt(i);
// if(mHeaderViews.get(key) == view)
// mHeaderViews.remove(key);
// if (mInnerAdapter instanceof MultiItemTypeAdapter)
// ((MultiItemTypeAdapter) mInnerAdapter).offset -= 1;
//
// if (mNotifyAdapter != null)
// mNotifyAdapter.notifyDataSetChanged();
// break;
//
//
int key = findHeaderKeyByView(view);
if (key != -1)
mHeaderViews.remove(key);
if (mInnerAdapter instanceof MultiItemTypeAdapter)
((MultiItemTypeAdapter) mInnerAdapter).offset -= 1;
if (mNotifyAdapter != null)
mNotifyAdapter.notifyDataSetChanged();
public int findHeaderKeyByView(View view)
for(int i=0; i<mHeaderViews.size(); i++)
int key = mHeaderViews.keyAt(i);
if(mHeaderViews.get(key) == view)
return key;
return -1;
这里的header和footer没有个数的限制。
添加section分区操作:
现在需求又来了 。需要对RecyclerView中的item进行分区操作,就比如说微信以B开头的姓名都放在B这个分区下,以C开头的名字,都在C这个分区下。比如说京东的这个界面
列表数据里面添加了分区。那么怎么操作呢?
这里对开源库SectionedRecyclerViewAdapter做了集成。并且添加的上面的ViewHolder,简化onCreateViewHolder中的数据绑定操作。
也就是代码中的recyclerview-section包中的部分。
Section的使用:
1) 创建自定义 Section 类集成自 StatelessSection
public class WeiboGridSection extends StatelessSection
private final List<NewsItem> list;
public WeiboGridSection(List<NewsItem> list)
super(R.layout.grid_item);
this.list = list;
@Override
public int getContentItemsTotal()
return list.getItems().size();
@Override
public ViewHolder getItemViewHolder(View view, int viewType)
return new ViewHolder(mContext, view);
@Override
public void onBindItemViewHolder(ViewHolder holder, final int position)
final NewsItem newsItem = list.get(position);
String itemImgUrl = newsItem.getImages().get(0);
final String name = newsItem.getTitle();
holder.setImageUrl(R.id.grid_item_iv, itemImgUrl);
holder.setText(R.id.grid_item_tv, name);
holder.getConvertView().setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Intent intent = new Intent(getActivity(), WebViewCommentActivity.class);
intent.putExtra(WebViewCommentActivity.WEB_VIEW_ITEM, newsItem);
startActivity(intent);
);
@Override
public ViewHolder getHeaderViewHolder(Context context, View view)
return new ViewHolder(mContext, view);
@Override
public void onBindHeaderViewHolder(ViewHolder holder)
holder.setText(R.id.section_header_tv, "微博关注");
holder.setImageResource(R.id.section_header_iv, R.mipmap.wb_focus);
2) 添加section到adapter,注意这里是SectionRVAdapter
// Create an instance of SectionedRecyclerViewAdapter
SectionRVAdapter sectionAdapter = new SectionRVAdapter();
// Add your Sections
sectionAdapter.addSection(new MySection());
// Set up your RecyclerView with the SectionRVAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);
看看界面效果
Section的代码实现
整个代码由于是对RecyclerView.Adapter
封装。所以需要关注的方法自然是getItemViewType
,onBindViewHolder
,createViewHolder
和getItemCount
四个方法,下面以这四个方法为切入点进行分析:
初始化需要关注的是这里使用hashMap对section进行存储。也就实现了后面的根据section的添加顺序依次展示Section到RecyclerView中。
onCreateViewHolder完成的是ViewHolder的创建
以上是关于真实项目运用-RecyclerView封装的主要内容,如果未能解决你的问题,请参考以下文章