RecyclerView了解—LayoutManger

持续整理中… …

有几篇文章我已经讲解过三大流程,还有一篇文章也深入分析了 RecyclerView 缓存 与 优化,RecyclerView的item动画(ItemAnimator)也写了相应的文章.
现在这篇文章 主要是讲解 LayoutManger的自定义与分析 的文章;
RecyclerView 的 measure 与 layout 都交给 LayoutManger 处理的.

先来看看 onMeasure 的代码:

protected void onMeasure(int widthSpec, int heightSpec) {
    
    
    ... ...
    // mLayout 是 LayoutManger
    // 无论代码如何执行,最后都会调用 onMeasure,
    // 然而 LinearLayoutManager 与 GridLayoutManager 并没有重写onMeasure,
    // 租后调用的就是 defaultOnMeasure 这个函数
    if (mLayout.isAutoMeasureEnabled()) {
    
    
        final int widthMode = MeasureSpec.getMode(widthSpec);
        final int heightMode = MeasureSpec.getMode(heightSpec);
       mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
       dispatchLayoutStep2(); 
    } else {
    
    
        if (mHasFixedSize) {
    
    
            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
           return;
        }
        ... ...
        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
    }
}

public void onMeasure(@NonNull Recycler recycler, @NonNull State state, int widthSpec,
        int heightSpec) {
    
    
    mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
}

void defaultOnMeasure(int widthSpec, int heightSpec) {
    
    
    final int width = LayoutManager.chooseSize(widthSpec,
            getPaddingLeft() + getPaddingRight(),
            ViewCompat.getMinimumWidth(this));
    final int height = LayoutManager.chooseSize(heightSpec,
            getPaddingTop() + getPaddingBottom(),
            ViewCompat.getMinimumHeight(this));
    // 设置宽高
    setMeasuredDimension(width, height);
}

public static int chooseSize(int spec, int desired, int min) {
    
    
    final int mode = View.MeasureSpec.getMode(spec);
    final int size = View.MeasureSpec.getSize(spec);
    switch (mode) {
    
    
        case View.MeasureSpec.EXACTLY:
            return size;
        case View.MeasureSpec.AT_MOST:
            return Math.min(size, Math.max(desired, min));
        case View.MeasureSpec.UNSPECIFIED:
        default:
            return Math.max(desired, min);
    }
}

再看看 onLayout 的代码:

protected void onLayout(boolean changed, int l, int t, int r, int b) {
    
    
    dispatchLayout();
}
void dispatchLayout() {
    
    
    if (mState.mLayoutStep == State.STEP_START) {
    
    
        dispatchLayoutStep1();
        mLayout.setExactMeasureSpecsFrom(this);
        dispatchLayoutStep2();
    } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
        || mLayout.getHeight() != getHeight()) {
    
    
        // First 2 steps are done in onMeasure but looks like we have to run again due to
        // changed size.
        mLayout.setExactMeasureSpecsFrom(this);
        dispatchLayoutStep2();
    } else {
    
    
        // always make sure we sync them (to ensure mode is exact)
        mLayout.setExactMeasureSpecsFrom(this);
    }
    dispatchLayoutStep3();    
}

关注下 dispatchLayoutStep2 这个方法,onLayoutChildren 这个方法非常重要…LayoutManger相关的布局都在这里处理的

private void dispatchLayoutStep2() {
    
    
    ... ...
    // Step 2: Run layout
    mState.mInPreLayout = false;
    mLayout.onLayoutChildren(mRecycler, mState);
    ... ...
}

一般来说,自定义LayoutManger 只需要了解 onLayoutChildren 基本就OK拉.

先来看看onLayoutChildren 的相关代码:

onLayoutChildre 会调用 detachAndScrapAttachedViews 分离 ViewHolder 存储到相应的缓存(Recycler)中去

这里拿出了 LinearLayoutManger 布局的流程(简单的过程):
fill ->

public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    
    
	// 缓存了解的文章也讲解过这个函数
    detachAndScrapAttachedViews(recycler);
    // 看看几个关键的绘制流程(从上往下,从下往上)
    if (mAnchorInfo.mLayoutFromEnd) {
    
    
    	// fill towards start
    	updateLayoutStateToFillStart(mAnchorInfo);
    	fill(recycler, mLayoutState, state, false);
    	// fill towards end
    	updateLayoutStateToFillEnd(mAnchorInfo);
    	fill(recycler, mLayoutState, state, false);
    } else {
    
    
    	// fill towards end
    	updateLayoutStateToFillEnd(mAnchorInfo);
    	fill(recycler, mLayoutState, state, false);
    	// fill towards start
    	updateLayoutStateToFillStart(mAnchorInfo);
        fill(recycler, mLayoutState, state, false);
    }
}

fill 函数里面的 layoutChunk 是从 LayoutState.next 里面取出 相关的 view(ItemView)
next 函数里面 的 getViewForPosition 是我们在 缓存的文章讲解过,先来看看代码

int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
        RecyclerView.State state, boolean stopOnFocusable) {
    
    
    // max offset we should set is mFastScroll + available
    final int start = layoutState.mAvailable;          
    ... ...
    while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
    
    
        ... ...
        layoutChunk(recycler, state, layoutState, layoutChunkResult);
        layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
    }
    return start - layoutState.mAvailable;         
}

// 从next获取相关 View(itemView),add..加入,measureChild..测量下,layoutDec...布局下
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
        LayoutState layoutState, LayoutChunkResult result) {
    
    
    View view = layoutState.next(recycler);    
    ... ...
    addView(view);
    ... ...
    addDisappearingView(view);
    ... ...
    measureChildWithMargins(view, 0, 0);
    ... ...
    layoutDecoratedWithMargins(view, left, top, right, bottom);       
    ... ...
    result.mFocusable = view.hasFocusable(); 
}
            
static class LayoutState {
    
    
    ... ...
    View next(RecyclerView.Recycler recycler) {
    
    
        // 在缓存了解的文章,我们已经分析过这里,给出跳转链接
        final View view = recycler.getViewForPosition(mCurrentPosition); 
    }    
}

addView 与 addDisappearingView(addViewInt) 有什么区别?
避免重复 add view,因为在 缓存里面只是 detach
addView 和 removeView方法,操作容器内的子视图数组,触发视图重绘制,触发子视图attach和detached窗体回调。 addViewInLayout 和 removeViewInLayou方法,与上面一样,只是不会重绘视图。
attachViewToParent 和 detachViewFromParent方法,只会操作容器内的子视图数组。

在这里插入图片描述

相关类 含义
AnchorInfo 绘制子View的时候,记录其位置,偏移量,方向等信息
LayoutChunkResult 加载子View结果情况的记录,比如已经填充的子View的数量
LayoutState 当前加载的状态记录,比如当前绘制的偏移量,屏幕还剩余多少空间等

推荐资料

https://github.com/wuyr/PathLayoutManager
https://www.kymjs.com/code/2016/07/10/01/

猜你喜欢

转载自blog.csdn.net/qw85525006/article/details/105828978