RecyclerView源码分析
Posted 小威少威
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RecyclerView源码分析相关的知识,希望对你有一定的参考价值。
RecyclerView发布已经挺久了,博客上也有好多优秀的使用教程,相比较ListView,RecyclerView的使用确实更加灵活,本文就针对RecyclerView的源码进行分析,分析一下它的工作原理,关于它的使用教程,大家可以去看下hongyang大神的博文,以下我就自己针对源码的查看写了自己的分析,如有不对的地方,欢迎提出指教。
我们首先看下RecyclerView这个类的第一条注释说明:A flexible view for providing a limited window into a large data set.可见该View的定义就是一种灵活的、能够装载大量数据的View。该类的源码有1w多行,我们就从使用它相关的代码进行查看:
recyclerView = (RecyclerView) findViewById(R.id.id_recyclerview);
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
recyclerView.setAdapter(new MyAdapter(this,datas));
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
这是我们常用的一段基本的代码,可知,我们会用到以下几个方法:setLayoutManager,(关键点,灵活的设置布局,简单),setAdapter,addItemDecoration。另外,还有这个类我们也一起看下:Recycler。
关于Recycler:
A Recycler is responsible for managing scrapped or detached item views for reufinal
public final class Recycler
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
private ArrayList<ViewHolder> mChangedScrap = null;
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
private final List<ViewHolder>
mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
private int mViewCacheMax = DEFAULT_CACHE_SIZE;
private RecycledViewPool mRecyclerPool;
private ViewCacheExtension mViewCacheExtension;
private static final int DEFAULT_CACHE_SIZE = 2;
它为RecyclerView中的item views提供了复用,用于管理已经废弃或与RecyclerView分离的(scrapped or detached)item view。
/**
* Set the @link LayoutManager that this RecyclerView will use.
*
* <p>In contrast to other adapter-backed views such as @link android.widget.ListView
* or @link android.widget.GridView, RecyclerView allows client code to provide custom
* layout arrangements for child views. These arrangements are controlled by the
* @link LayoutManager. A LayoutManager must be provided for RecyclerView to function.</p>
*
* <p>Several default strategies are provided for common uses such as lists and grids.</p>
*
* @param layout LayoutManager to use
*/
public void setLayoutManager(LayoutManager layout)
if (layout == mLayout)
return;
stopScroll();
// TODO We should do this switch a dispachLayout pass and animate children. There is a good
// chance that LayoutManagers will re-use views.
if (mLayout != null)
if (mIsAttached)
mLayout.dispatchDetachedFromWindow(this, mRecycler);
mLayout.setRecyclerView(null);
mRecycler.clear();
mChildHelper.removeAllViewsUnfiltered();
mLayout = layout;
if (layout != null)
if (layout.mRecyclerView != null)
throw new IllegalArgumentException("LayoutManager " + layout +
" is already attached to a RecyclerView: " + layout.mRecyclerView);
mLayout.setRecyclerView(this);
if (mIsAttached)
mLayout.dispatchAttachedToWindow(this);
requestLayout();
我们看这句代码:mLayout.setRecyclerView(this);
继续追踪下去:
/**
* These measure specs might be the measure specs that were passed into RecyclerView's
* onMeasure method OR fake measure specs created by the RecyclerView.
* For example, when a layout is run, RecyclerView always sets these specs to be
* EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
*/
private int mWidthSpec, mHeightSpec;
void setRecyclerView(RecyclerView recyclerView)
if (recyclerView == null)
mRecyclerView = null;
mChildHelper = null;
mWidthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
mHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
else
mRecyclerView = recyclerView;
mChildHelper = recyclerView.mChildHelper;
mWidthSpec = MeasureSpec
.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY);
mHeightSpec = MeasureSpec
.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY);
也就是测量RecyclerView 中itemView高度和宽度,然后对其绘制,可以实现水平滚动、垂直滚动、方块表格等列表形式。具体可以看LayoutManager这个类。
接下来我们看下这句代码:
recyclerView.setAdapter(new MyAdapter(this,datas));
可知,它是为recyclerView存放数据集的,其中MyAdapter是我继承RecyclerView.Adapter
/**
* Base class for an Adapter
*
* <p>Adapters provide a binding from an app-specific data set to views that are displayed
* within a @link RecyclerView.</p>
*/
public static abstract class Adapter<VH extends ViewHolder>
private final AdapterDataObservable mObservable = new AdapterDataObservable();
private boolean mHasStableIds = false;
/**
* Called when RecyclerView needs a new @link ViewHolder of the given type to represent
* an item.
* <p>
* This new ViewHolder should be constructed with a new View that can represent the items
* of the given type. You can either create a new View manually or inflate it from an XML
* layout file.
* <p>
* The new ViewHolder will be used to display items of the adapter using
* @link #onBindViewHolder(ViewHolder, int, List). Since it will be re-used to display
* different items in the data set, it is a good idea to cache references to sub views of
* the View to avoid unnecessary @link View#findViewById(int) calls.
*
* @param parent The ViewGroup into which the new View will be added after it is bound to
* an adapter position.
* @param viewType The view type of the new View.
*
* @return A new ViewHolder that holds a View of the given view type.
* @see #getItemViewType(int)
* @see #onBindViewHolder(ViewHolder, int)
*/
public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
public abstract void onBindViewHolder(VH holder, int position);
public void onBindViewHolder(VH holder, int position, List<Object> payloads)
onBindViewHolder(holder, position);
/**
* This method calls @link #onCreateViewHolder(ViewGroup, int) to create a new
* @link ViewHolder and initializes some private fields to be used by RecyclerView.
*
* @see #onCreateViewHolder(ViewGroup, int)
*/
public final VH createViewHolder(ViewGroup parent, int viewType)
TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
final VH holder = onCreateViewHolder(parent, viewType);
holder.mItemViewType = viewType;
TraceCompat.endSection();
return holder;
......
我们可以重写该类的onBindViewHolder方法,进行自己的数据处理,而在onCreateViewHolder方法中我们可以读取item文件进行设置。相比ListView的优点就是RecyclerView直接将ViewHolder内置:
ViewHolder 部分代码:
/**
* A ViewHolder describes an item view and metadata about its place within the RecyclerView.
*
* <p>@link Adapter implementations should subclass ViewHolder and add fields for caching
* potentially expensive @link View#findViewById(int) results.</p>
*
* <p>While @link LayoutParams belong to the @link LayoutManager,
* @link ViewHolder ViewHolders belong to the adapter. Adapters should feel free to use
* their own custom ViewHolder implementations to store data that makes binding view contents
* easier. Implementations should assume that individual item views will hold strong references
* to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
* strong references to extra off-screen item views for caching purposes</p>
*/
public static abstract class ViewHolder
public final View itemView;
int mPosition = NO_POSITION;
int mOldPosition = NO_POSITION;
long mItemId = NO_ID;
int mItemViewType = INVALID_TYPE;
int mPreLayoutPosition = NO_POSITION;
// The item that this holder is shadowing during an item change event/animation
ViewHolder mShadowedHolder = null;
// The item that is shadowing this holder during an item change event/animation
ViewHolder mShadowingHolder = null;
.......
提供了很多属性。
ItemDecoration:
/**
* An ItemDecoration allows the application to add a special drawing and layout offset
* to specific item views from the adapter's data set. This can be useful for drawing dividers
* between items, highlights, visual grouping boundaries and more.
*
* <p>All ItemDecorations are drawn in the order they were added, before the item
* views (in @link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()
* and after the items (in @link ItemDecoration#onDrawOver(Canvas, RecyclerView,
* RecyclerView.State).</p>
*/
public static abstract class ItemDecoration
/**
* Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
* Any content drawn by this method will be drawn before the item views are drawn,
* and will thus appear underneath the views.
*
* @param c Canvas to draw into
* @param parent RecyclerView this ItemDecoration is drawing into
* @param state The current state of RecyclerView
*/
public void onDraw(Canvas c, RecyclerView parent, State state)
onDraw(c, parent);
/**
* @deprecated
* Override @link #onDraw(Canvas, RecyclerView, RecyclerView.State)
*/
@Deprecated
public void onDraw(Canvas c, RecyclerView parent)
/**
* Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
* Any content drawn by this method will be drawn after the item views are drawn
* and will thus appear over the views.
*
* @param c Canvas to draw into
* @param parent RecyclerView this ItemDecoration is drawing into
* @param state The current state of RecyclerView.
*/
public void onDrawOver(Canvas c, RecyclerView parent, State state)
onDrawOver(c, parent);
/**
* @deprecated
* Override @link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)
*/
@Deprecated
public void onDrawOver(Canvas c, RecyclerView parent)
/**
* @deprecated
* Use @link #getItemOffsets(Rect, View, RecyclerView, State)
*/
@Deprecated
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)
outRect.set(0, 0, 0, 0);
/**
* Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
* the number of pixels that the item view should be inset by, similar to padding or margin.
* The default implementation sets the bounds of outRect to 0 and returns.
*
* <p>
* If this ItemDecoration does not affect the positioning of item views, it should set
* all four fields of <code>outRect</code> (left, top, right, bottom) to zero
* before returning.
*
* <p>
* If you need to access Adapter for additional data, you can call
* @link RecyclerView#getChildAdapterPosition(View) to get the adapter position of the
* View.
*
* @param outRect Rect to receive the output.
* @param view The child view to decorate
* @param parent RecyclerView this ItemDecoration is decorating
* @param state The current state of RecyclerView.
*/
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)
getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
parent);
像我们以前用ListView都是在ItemView中去写好布局,现在我们的ItemView和要装饰的UI是分离出来的,用ItemDecoration这个方法即可装饰ItemView,比如项之间的分割线、Margin、绘制颜色等。我们也可以自定义ItemDecoration引用自己绘制的UI进行使用,功能上更加强大。
以上是关于RecyclerView源码分析的主要内容,如果未能解决你的问题,请参考以下文章