RecyclerView源码分析

来源:互联网 发布:网络贷款利息计算器 编辑:程序博客网 时间:2024/05/16 23:58

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

1 0
原创粉丝点击