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进行使用,功能上更加强大。

    原文作者:小威少威
    原文地址: https://blog.csdn.net/u013606974/article/details/51837314
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞