RecycleView源码分析

本文详细分析了RecyclerView的原理,包括其在数据更新时如何通过Adapter通知刷新,LayoutManager如何布局,ItemAnimator如何处理动画,以及Recycler如何进行item的回收和复用。文中特别提到了measure、layout、draw的流程,并以LinearLayoutManager为例解释了布局填充的过程,同时阐述了滑动事件的处理,包括drag滑动和惯性滑动的实现机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、RecycleView概述

1、简介

       A flexible view for providing a limited window into a large data set.

       与RecycleView比较相关的几个概念

  • Adapter: A subclass of RecyclerView.Adapter responsible for providing views that represent items in a data set.
  • Position: The position of a data item within an Adapter.
  • Index: The index of an attached child view as used in a call to getChildAt(int). Contrast with Position.
  • Binding: The process of preparing a child view to display data corresponding to a position within the adapter.
  • Recycle (view): A view previously used to display data for a specific adapter position may be placed in a cache for later reuse to display the same type of data again later. This can drastically improve performance by skipping initial layout inflation or construction.
  • Scrap (view): A child view that has entered into a temporarily detached state during layout. Scrap views may be reused without becoming fully detached from the parent RecyclerView, either unmodified if no rebinding is required or modified by the adapter if the view was considered dirty.
  • Dirty (view): A child view that must be rebound by the adapter before being displayed.

        关于position的一些注意点和说明:

there are two types of position related methods in RecyclerView:

  • layout position: Position of an item in the latest layout calculation. This is the position from the LayoutManager's perspective.
  • adapter position: Position of an item in the adapter. This is the position from the Adapter's perspective.

These two positions are the same except the time between dispatching adapter.notify* events and calculating the updated layout.

2、整体架构

  • RecycleView也是通过观察者模式,数据更新时,通过Adapter通知RecycleView更新。RecycleView为Observer,Adapter为Subject
  • RecycleView的item的布局主要由LayoutManager完成
  • RecycleView每个Item的动画主要由ItemAnimator完成
  • Recycler完成item的回收
  • State、AdapterHelper和ChildHelper是内部几个比较重要的类,分别管理部分数据和业务

二、源码分析

备注:以下所有代码均基于Android 23版本分析

1、measure、layout、draw

(1) measure 过程

  • 完成RecycleView这个ViewGroup的measure过程
  • 更新mState全局变量的状态,包括当前是否为preLayout,itemCount为多少等

(2) layout 过程

      RecycleView的layout过程,在真正完成layout的操作之外,还包含两个过程,分别为preLayout何和postLayout。

  • preLayout过程:记录数据改变之前,item view相关的信息
  • postLayout过程:记录数据改变之后,item view相关信息,以及对比preLayout中存储的item信息,完成相应动画
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    ......
    dispatchLayout();
    ......
}
void dispatchLayout() {
    ......
    if (mState.mRunSimpleAnimations) {
        // Step 0: Find out where all non-removed items are, pre-layout
        ......
    }
 
 
    if (mState.mRunPredictiveAnimations) {      //pre-layout
        // Step 1: run prelayout: This will use the old positions of items. The layout manager
        // is expected to layout everything, even removed items (though not to add removed
        // items back to the container). This gives the pre-layout position of APPEARING views
        // which come into existence as part of the real layout.
        ......
    } else {
        //remove the info
        ......
    }
 
 
    // Step 2: Run layout
    mState.mInPreLayout = false;
    mLayout.onLayoutChildren(mRecycler, mState);
 
 
    if (mState.mRunSimpleAnimations) {
        // Step 3: Find out where things are now, post-layout
        ......
 
        // Step 4: Animate DISAPPEARING and REMOVED items
        ......
 
        // Step 5: Animate APPEARING and ADDED items
        ......
 
        // Step 6: Animate PERSISTENT items
        ......
 
        // Step 7: Animate CHANGING items
        ......
    }
 
 
    ......
}
  • 在layout过程中,主要是调用mLayoutManager的onLayoutChildren完成布局工作
  • 如果RecycleView的item再在变化过程中需要展现动画,则在真正完成布局之前需要记录相关信息(step0和step1),完成布局之后需要完成动画(step3到step7)
  • 如果不需要动画,RecycleView可以省去不少工作,性能会有所提升

下面以LinearLayoutManager举例说明

// layout algorithm(布局算法说明):
// 1) by checking children and other variables, find an anchor coordinate and an anchor item position.
// 2) fill towards start, stacking from bottom
// 3) fill towards end, stacking from top
// 4) scroll to fulfill requirements like stack from bottom.

@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    // layout algorithm:
    ...... 
 
    // calculate anchor position and coordinate
    mAnchorInfo.reset();
    mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd;
    updateAnchorInfoForLayout(recycler, state, mAnchorInfo);
 
    //暂时将RecycleView中的所有child全部detach,并将入到mAttachScrap或者mChangeScrap
    detachAndScrapAttachedViews(recycler);
 
    //根据相关的参数,计算是否从end开始layout
    if (mAnchorInfo.mLayoutFromEnd) {
        // fill towards start
        fill(recycler, mLayoutState, state, false);
        ......
 
        // fill towards end
        fill(recycler, mLayoutState, state, false);
        ......
 
        if (mLayoutState.mAvailable > 0) {
            // end could not consume all. add more items towards start
            fill(recycler, mLayoutState, state, false);
        }
    } else {
        //相反过程
    }
 
    //If necessary, layouts new items for predictive animations
    layoutForPredictiveAnimations(recycler, state, startOffset, endOffset);
 
    ......
}

在onLayoutChildren()函数中,根据layout算法,对item view进行layout。其中主要工作完成在fill()函数中。

下面看看fill()函数


int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
        RecyclerView.State state, boolean stopOnFocusable) {
 
    ......
    final int start = layoutState.mAvailable;
    int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
    LayoutChunkResult layoutChunkResult = new LayoutChunkResult();
    while (remainingSpace > 0 && layoutState.hasMore(state)) {
        layoutChunkResult.resetInternal();
        layoutChunk(recycler, state, layoutState, layoutChunkResult);    //layout每个item,这里是chunk
         
        /**
         * Consume the available space if:
         * * layoutChunk did not request to be ignored
         * * OR we are laying out scrap children
         * * OR we are not doing pre-layout
         */
        ......
 
        if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) {
            ......
            recycleByLayoutState(recycler, layoutState);     //回收超出显示区域的item view
        }
        ......
    }
 
 
    //Number of pixels that it added. Useful for scroll functions.
    return start - layoutState.mAvailable;
}
  • fill()函数工作主要集中在while循环中,从锚点开始对每个item进行布局,直到界面已经没有空间(remainingSpace > 0 条件不满足)或者没有数据了(layoutState.hasMore(state)条件不满足)
  • 对正在滑动过程中,回收超出界面显示区域的item

 

接下来继续看看layoutChunk()函数


void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
        LayoutState layoutState, LayoutChunkResult result) {
    View view = layoutState.next(recycler);     //获取下一个用于layout的item,涉及到复用item的获取流程,稍后分析
     
    ......
    if (layoutState.mScrapList == null) {    //真正将item添加到RecycleView
        if (mShouldReverseLayout == (layoutState.mLayoutDirection == LayoutState.LAYOUT_START)) {
            addView(view);
        } else {
            addView(view, 0);
        }
    } else {     //动画过程,增加即将消失的item
        if (mShouldReverseLayout == (layoutState.mLayoutDirection == LayoutState.LAYOUT_START)) {
            addDisappearingView(view);
        } else {
            addDisappearingView(view, 0);
        }
    }
 
 
    //measure
    measureChildWithMargins(view, 0, 0); 
  
    ......
 
 
    //layout
    layoutDecorated(view, left + params.leftMargin, top + params.topMargin,
            right - params.rightMargin, bottom - params.bottomMargin);
     
}
  • 获取需要进行layout的item
  • 将item添加到view tree上
  • measure
  • layout

可以看到,在layoutChunk()函数中就真正完成了把view添加到view上,并且measure和layout,从而将item放在了正确的位置。

(3)draw

         RecycleView的绘制过程主要在draw()和onDraw()两个函数中完成

  • draw函数中,主要完成上下左右四个方向的glow的绘制
  • onDraw函数中,主要完成ItemDecoration的绘制

2、滑动

  • 滑动事件主要集中在onTouchEvent()中处理
  • 滑动涉及三个状态的轮转,分别是SCROLL_STATE_IDLE, SCROLL_STATE_DRAGGING, SCROLL_STATE_SETTLING
  • 滑动主要涉及两个阶段,手指拖动阶段和手指松开阶段
@Override
public boolean onTouchEvent(MotionEvent e) {
    ......
 
    switch (action) {
        case MotionEvent.ACTION_DOWN: {
            ......
        } break;
 
        case MotionEventCompat.ACTION_POINTER_DOWN: {
            ......
        } break;
 
        case MotionEvent.ACTION_MOVE: {
            final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
            ......
 
            if (mScrollState != SCROLL_STATE_DRAGGING) { 
                boolean startScroll = false;
                if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {   //判断是否触发横向滑动
                    if (dx > 0) {
                        dx -= mTouchSlop;
                    } else {
                        dx += mTouchSlop;
                    }
                    startScroll = true;
                }
                if (canScrollVertically && Math.abs(dy) > mTouchSlop) {     //判断是否触发纵向滑动
                    if (dy > 0) {
                        dy -= mTouchSlop;
                    } else {
                        dy += mTouchSlop;
                    }
                    startScroll = true;
                }
                if (startScroll) {     
                    final ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);    //触发滑动后,请求不拦截事件
                    }
                    setScrollState(SCROLL_STATE_DRAGGING);   //更新状态为SCROLL_STATE_DRAGGING
                }
            }
 
            if (mScrollState == SCROLL_STATE_DRAGGING) {     //滑动状态下
                mLastTouchX = x - mScrollOffset[0];
                mLastTouchY = y - mScrollOffset[1];
 
                if (scrollByInternal(
                        canScrollHorizontally ? dx : 0,
                        canScrollVertically ? dy : 0,
                        vtev)) {       //scrollByInternal()函数中完成滑动
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
            }
        } break;
 
        case MotionEventCompat.ACTION_POINTER_UP: {
            onPointerUp(e);
        } break;
 
        case MotionEvent.ACTION_UP: {
            mVelocityTracker.addMovement(vtev);
            eventAddedToVelocityTracker = true;
            mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
            final float xvel = canScrollHorizontally ?
                    -VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0;
            final float yvel = canScrollVertically ?
                    -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0;
            if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {     //fling()函数中完成松手后的滑动
                setScrollState(SCROLL_STATE_IDLE);
            }
            resetTouch();
        } break;
 
        case MotionEvent.ACTION_CANCEL: {
            cancelTouch();
        } break;
    }
 
    ......
 
    return true;
}
  • ACTION_DOWN
  • ACTION_POINT_DOWN
    完成相关事件位置等信息的存储
  • ACTION_MOVE
    1)如果当前不是SCROLL_STATE_DRAGGING状态,则进入滑动触发判断
    2)如果当前是SCROLL_STATE_DRAGGING状态,则调用scrollByInternal()函数完成滑动
  • ACTION_UP
    根据当前滑动速度,调用fling()函数完成松手后的滑动

 

(1)drag滑动

          drag滑动主要在scrollByInternal()函数中实现,下面从这个函数开始分

boolean scrollByInternal(int x, int y, MotionEvent ev) {
    ......
 
    if (mAdapter != null) {
        ......
        if (x != 0) {    //完成x方向的滑动
            consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
            unconsumedX = x - consumedX;
        }
        if (y != 0) {    //完成y方向的滑动
            consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
            unconsumedY = y - consumedY;
        }
        ......
    }
     
    if (consumedX != 0 || consumedY != 0) {    //分发scroll事件
        dispatchOnScrolled(consumedX, consumedY);
    }
    ......
    return consumedX != 0 || consumedY != 0;
}
  • 该方法为控件内部的方法,输入经过校验合法
  • 调用mLayout的scrollHorizontaillyBy()和scrollVerticallyBy()方法分别完成水平和竖直方向上的滑动
  • 如果有滑动距离,分发滑动事件给Listener

下面以scrollVerticallyBy()为例,看看滑动的具体实现

@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
        RecyclerView.State state) {
    if (mOrientation == HORIZONTAL) {
        return 0;
    }
    return scrollBy(dy, recycler, state);
}

int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
    //计算scrolled
    ......
     
    mOrientationHelper.offsetChildren(-scrolled);
    return scrolled;
}
public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) {
    return new OrientationHelper(layoutManager) {
        ......
 
        @Override
        public void offsetChildren(int amount) {
            mLayoutManager.offsetChildrenVertical(amount);
        }
 
        ......
    };
}
public void offsetChildrenVertical(int dy) {
    if (mRecyclerView != null) {
        mRecyclerView.offsetChildrenVertical(dy);
    }
}
public void offsetChildrenVertical(int dy) {
    final int childCount = mChildHelper.getChildCount();
    for (int i = 0; i < childCount; i++) {
        mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
    }
}

通过连续的多次调用,最终可以看到,就是滑动最终实现是改变了每个item的bounds

 

(2)惯性滑动实现

         松手惯性滑动,主要在fling()函数中完成。

/**
 * Begin a standard fling with an initial velocity along each axis in pixels per second.
 * If the velocity given is below the system-defined minimum this method will return false
 * and no fling will occur.
 */
public boolean fling(int velocityX, int velocityY) {
    ......
 
    if (!dispatchNestedPreFling(velocityX, velocityY)) {
        final boolean canScroll = canScrollHorizontal || canScrollVertical;
 
        if (canScroll) {
            velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
            velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
            mViewFlinger.fling(velocityX, velocityY);
            return true;
        }
    }
    return false;
}
  • 从函数注释可以看出,具有一定初速度才会触发惯性滑动,否则没有任何效果
  • 在该函数中,主要完成根据参数来计算滑动的速度
  • 滑动操作主要是调用mViewFlinger的fling()函数完成
public void fling(int velocityX, int velocityY) {
    setScrollState(SCROLL_STATE_SETTLING);
    mLastFlingX = mLastFlingY = 0;
    mScroller.fling(0, 0, velocityX, velocityY,
            Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
    postOnAnimation();
}
  • 在fling()函数中,调用Scroller完成滑动的绘制,然后会调用postOnAnimation()
void postOnAnimation() {
    if (mEatRunOnAnimationRequest) {
        mReSchedulePostAnimationCallback = true;
    } else {
        removeCallbacks(this);
        ViewCompat.postOnAnimation(RecyclerView.this, this);
    }
}
  • 接下来会进入上面代码块的else分支,post一个Runnable,这个Runnable就是this参数,接下来可以看到run()方法
public void run() {
    if (scroller.computeScrollOffset()) {
        ......
        if (mAdapter != null) {
            ......
            if (dx != 0) {
                hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
                overscrollX = dx - hresult;
            }
            if (dy != 0) {
                vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
                overscrollY = dy - vresult;
            }
            ......
        }
         
        if (overscrollX != 0 || overscrollY != 0) {
            //处理over scroll
            ......
        }
 
        if (hresult != 0 || vresult != 0) {
            //分发scroll事件
            dispatchOnScrolled(hresult, vresult);
        }
 
        ......
 
        if (scroller.isFinished() || !fullyConsumedAny) {
            setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
        } else {
            postOnAnimation();
        }
    }
 
    ......
}
  • 在横向和竖向是否可以滑动,可以滑动则调用mLayout完成滑动(具体的滑动分析之前已有,不再赘述)
  • 处理over scroll
  • 分发scroll事件
  • 根据当前状态,继续完成滑动或者切换到SCROLL_STATE_IDLE

上面代码的处理逻辑,和手指拖动滑动,scrollInternal非常相似,代码有大部分重复。

 

3、回收与复用

     RecycleView以ViewHolder为单位进行回收,回收的item包含四级缓存,分别是

  • scapped:屏幕上的item
  • cached:屏幕之外的item
  • exCached:自定义实现
  • recycledPool:用于多RecycleView的复用

     四级缓存优先级从上往下依次递减,复用item时依次从优先级中获取item,如果没有获取到才create。

(1)回收

1)超出屏幕的回收

         在onLayoutChildren()函数中,调用fill()函数填充布局时,会根据滑动的距离调用recycleByLayoutState()函数进行item的回收。

private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
    if (!layoutState.mRecycle) {
        return;
    }
 
    if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {    //如果从start开始布局,则从end开始回收
        recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
    } else {   //相反
        recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
    }
}

下面我们取第一种情况进行分析

private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) {
    ......
 
    if (mShouldReverseLayout) {
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (mOrientationHelper.getDecoratedStart(child) < limit) {// stop here
                recycleChildren(recycler, 0, i);   //执行回收
                return;
            }
        }
    } else {
        for (int i = childCount - 1; i >= 0; i--) {
            View child = getChildAt(i);
            if (mOrientationHelper.getDecoratedStart(child) < limit) {// stop here
                recycleChildren(recycler, childCount - 1, i);    //执行回收
                return;
            }
        }
    }
}

回收执行时在recycleChildren()函数中完成的

//回收start到end之间的item
private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
    if (startIndex == endIndex) {
        return;
    }
    if (DEBUG) {
        Log.d(TAG, "Recycling " + Math.abs(startIndex - endIndex) + " items");
    }
    if (endIndex > startIndex) {
        for (int i = endIndex - 1; i >= startIndex; i--) {
            removeAndRecycleViewAt(i, recycler);
        }
    } else {
        for (int i = startIndex; i > endIndex; i--) {
            removeAndRecycleViewAt(i, recycler);
        }
    }
}
public void removeAndRecycleViewAt(int index, Recycler recycler) {
    final View view = getChildAt(index);
    removeViewAt(index);    //从RecycleView中删除index所在的item
    recycler.recycleView(view);    //回收删除的item
}
/**
 * Recycle a detached view. The specified view will be added to a pool of views
 * for later rebinding and reuse.
 *
 * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
 * View is scrapped, it will be removed from scrap list.</p>
 */
public void recycleView(View view) {
    // This public recycle method tries to make view recycle-able since layout manager
    // intended to recycle this view (e.g. even if it is in scrap or change cache)
    ViewHolder holder = getChildViewHolderInt(view);
    ......
  
    recycleViewHolderInternal(holder);     //真正执行回收
}
void recycleViewHolderInternal(ViewHolder holder) {
    ......
 
    if (forceRecycle || holder.isRecyclable()) {
        if (!holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED |
                ViewHolder.FLAG_CHANGED | ViewHolder.FLAG_UPDATE)) {    //将旧的cache view放到RecycleView Pool
            // Retire oldest cached view
            final int cachedViewSize = mCachedViews.size();
            if (cachedViewSize == mViewCacheMax && cachedViewSize > 0) {    //如果当前cacheViewSize已经达到最大(2个),则把第一个放入RecycleView Pool
                recycleCachedViewAt(0);
            }
            if (cachedViewSize < mViewCacheMax) {
                mCachedViews.add(holder);
                cached = true;
            }
        }
        if (!cached) {
            addViewHolderToRecycledViewPool(holder);
            recycled = true;
        }
    }
    ......
}

这里就真正把holder回收到cache中,如果cache中已经有数据了,则将数据放入RecycleView Pool中。

上面流程就完成了将移出到屏幕外的item回收到cache中,并将cache中的数据回收到RecycleView Pool中了。

 

2)layout时候的回收

      在RecycleView的onLayoutChildren()中,会将所有的view暂时scrap。从detachAndScrapAttachedViews()函数开始分析

/**
 * Temporarily detach and scrap all currently attached child views. Views will be scrapped
 * into the given Recycler. The Recycler may prefer to reuse scrap views before
 * other views that were previously recycled.
 */
public void detachAndScrapAttachedViews(Recycler recycler) {
    final int childCount = getChildCount();
    for (int i = childCount - 1; i >= 0; i--) {
        final View v = getChildAt(i);
        scrapOrRecycleView(recycler, i, v);
    }
}
private void scrapOrRecycleView(Recycler recycler, int index, View view) {
    final ViewHolder viewHolder = getChildViewHolderInt(view);
    ......
 
    detachViewAt(index);     //临时删除index对应的view
    recycler.scrapView(view);     //将view回收到scrap
}

void scrapView(View view) {
    final ViewHolder holder = getChildViewHolderInt(view);
    holder.setScrapContainer(this);
    if (!holder.isChanged() || !supportsChangeAnimations()) {
        mAttachedScrap.add(holder);
    } else {
        mChangedScrap.add(holder);
    }
}

        下面我们看看在屏幕上滑动时,使用复用数据的情况。从前面滑动过程,layoutChunk()函数中,通过layoutState.next()开始分析

View next(RecyclerView.Recycler recycler) {
    if (mScrapList != null) {
        return nextViewFromScrapList();     //从Scrap List中获取一个view,用于layout
    }
    final View view = recycler.getViewForPosition(mCurrentPosition);      //从position获取数据
    mCurrentPosition += mItemDirection;
    return view;
}

View getViewForPosition(int position, boolean dryRun) {
    ......
 
    boolean fromScrap = false;
    ViewHolder holder = null;
    // 0) If there is a changed scrap, try to find from there  //0:从mChangeScrap中获取
    if (mState.isPreLayout()) {
        holder = getChangedScrapViewForPosition(position);
        fromScrap = holder != null;
    }
 
    // 1) Find from scrap by position   //1:根据position从scrap中获取,包括mAttachedScrap和mCahceView中
    if (holder == null) {
        holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
        if (holder != null) {
            ......
        }
    }
 
    if (holder == null) {
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        final int type = mAdapter.getItemViewType(offsetPosition);
        // 2) Find from scrap via stable ids, if exists      //2:根据stable id从scrap中获取,包括mAttachedScrap和mCahceView中
        if (mAdapter.hasStableIds()) {
            holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
            if (holder != null) {
                // update position
                holder.mPosition = offsetPosition;
                fromScrap = true;
            }
        }
 
        if (holder == null && mViewCacheExtension != null) {
            final View view = mViewCacheExtension
                    .getViewForPositionAndType(this, position, type);    //3:从exCache中获取
            if (view != null) {
                holder = getChildViewHolder(view);
                ......
            }
        }
        if (holder == null) { // fallback to recycler
            // try recycler.
            // Head to the shared pool.
            holder = getRecycledViewPool().getRecycledView(type);    //4.从RecycleView Pool中获取
            ......
        }
        if (holder == null) {
            holder = mAdapter.createViewHolder(RecyclerView.this, type);   //5.调用createViewHolder创建
            ......
        }
    }
     
    if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        mAdapter.bindViewHolder(holder, offsetPosition);     //调用bindViewHolder
        ......
    }
 
    //下面获取item的布局参数LayoutParam
    final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
    final LayoutParams rvLayoutParams;
    if (lp == null) {
        rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();    //没有Lp则生产默认的
        holder.itemView.setLayoutParams(rvLayoutParams);
    } else if (!checkLayoutParams(lp)) {
        rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
        holder.itemView.setLayoutParams(rvLayoutParams);
    } else {
        rvLayoutParams = (LayoutParams) lp;
    }
    rvLayoutParams.mViewHolder = holder;
    return holder.itemView;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值