文章标题

来源:互联网 发布:linux的java环境变量 编辑:程序博客网 时间:2024/06/05 07:41

添加View
添加一个View是调用layoutChunk方法来完成的,让我们来看看这个layoutChunk方法:

    void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,            LayoutState layoutState, LayoutChunkResult result) {            // 获取到view对象。        View view = layoutState.next(recycler);        if (view == null) {            if (DEBUG && layoutState.mScrapList == null) {                throw new RuntimeException("received null view when unexpected");            }            // if we are laying out views in scrap, this may return null which means there is            // no more items to layout.            // 如果从回收的view对象里面scraplist 里面获取view的话,真的有可能出现            // null 的情况,表示没有更多的item对象给你布局。添加            result.mFinished = true;            return;        }        // 从这里获取到布局的参数        LayoutParams params = (LayoutParams) view.getLayoutParams();        if (layoutState.mScrapList == null) {            // 从头上面加view 下面是从后面加view            if (mShouldReverseLayout == (layoutState.mLayoutDirection                    == LayoutState.LAYOUT_START)) {                addView(view);            } else {                addView(view, 0);            }        } else { // 表示mScrapList 不为空            if (mShouldReverseLayout == (layoutState.mLayoutDirection                    == LayoutState.LAYOUT_START)) {                    // 有缓存从缓存中获取                addDisappearingView(view);            } else {                addDisappearingView(view, 0);            }        }        // 测量view的宽高        measureChildWithMargins(view, 0, 0);        // 这个方法找不到 这个方法我理解的是 测量它占用的位置是多少。相当于就是消费了多少距离        result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);        int left, top, right, bottom;        if (mOrientation == VERTICAL) {        // 方向垂直  布局从右边到左        // 这里是获取  上下、左右 四个方向上的位置            if (isLayoutRTL()) {                right = getWidth() - getPaddingRight();                /**                * 这个是getDecoratedMeasurementInOther的方法解释                *Returns the space occupied by this View in the perpendicular orientation including                * decorations and margins.                * 返回这个view在垂直方向方向上的占用,包括margin的距离,但是根据下面的用法                * 好像不是这个意思                */                left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);            } else {                left = getPaddingLeft();                right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);            }            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {            // mLayoutState.mOffset 用于在添加布局的时候,根据它来确定被添加子View的布局位置。            // 就是表明在添加的时候,从哪里开始添加。                bottom = layoutState.mOffset;                // 这个滑动方向是从下往上翻,表明开始位置从下往上开始添加的                // 开始位置减去view占用的距离,就是顶部的位置。下面也是差不多的道理。                top = layoutState.mOffset - result.mConsumed;            } else {                top = layoutState.mOffset;                bottom = layoutState.mOffset + result.mConsumed;            }        } else {            top = getPaddingTop();            bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {                right = layoutState.mOffset;                left = layoutState.mOffset - result.mConsumed;            } else {                left = layoutState.mOffset;                right = layoutState.mOffset + result.mConsumed;            }        }        // We calculate everything with View's bounding box (which includes decor and margins)        // To calculate correct layout position, we subtract margins.        // 我们计算每个装 view的大小。减掉 margin        layoutDecorated(view, left + params.leftMargin, top + params.topMargin,                right - params.rightMargin, bottom - params.bottomMargin);        if (DEBUG) {            Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"                    + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"                    + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin));        }        // Consume the available space if the view is not removed OR changed        // 如果到边界了没有移动啥的。吧这个距离消费掉。        if (params.isItemRemoved() || params.isItemChanged()) {            result.mIgnoreConsumed = true;        }        result.mFocusable = view.isFocusable();    }
        /**         * Gets the view for the next element that we should layout.         * Also updates current item index to the next item, based on {@link #mItemDirection}         *         * @return The next element that we should layout.         * 这里是要添加到recycle view 里面的view子对象,从这里获取         */        View next(RecyclerView.Recycler recycler) {            if (mScrapList != null) {                return nextViewFromScrapList();            }            final View view = recycler.getViewForPosition(mCurrentPosition);            mCurrentPosition += mItemDirection;            return view;        }

layoutChunk 方法执行完毕之后,继续分析。

    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;        if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) {            // TODO ugly bug fix. should not happen            if (layoutState.mAvailable < 0) {                layoutState.mScrollingOffset += layoutState.mAvailable;            }            recycleByLayoutState(recycler, layoutState);        }        int remainingSpace = layoutState.mAvailable + layoutState.mExtra;        LayoutChunkResult layoutChunkResult = new LayoutChunkResult();        /**         *          * // 这个是就是 layoutState.mInfinite的原始的定义的位置,自己不设置成无限大就没问题         * // 基本上都是false                mOrientationHelper.getMode() == View.MeasureSpec.UNSPECIFIED                && mOrientationHelper.getEnd() == 0;         *          * layoutState.hasMore(state) 表示当前adapter里面是否还有更多的数据要显示。         *          * remainingSpace 定义         * int remainingSpace = layoutState.mAvailable + layoutState.mExtra;         * layoutState.mAvailable 解释:Number of pixels that we should fill, in the          * layout direction 就是在我们布局的方向上,有多少地方我们要去填充的,应该表示移动的距离。         *          * layoutState.mExtra 解释:Used if you want to pre-layout items that are not yet          * visible.          * The difference with {@link #mAvailable} is that, when recycling, distance         * laid out for          * {@link #mExtra} is not considered to avoid recycling visible children.         * 如果在还没有显示之前提前布局就用到这个值,不同于mAvailable的是,当正在回收的时候,         * 距离布局到mExtra 是不考虑避开正在显示的子类         * 根据下面使用的方法的意思,应该是最终可以移动的距离,与当前这次移动的距离的值,如果大于0,         * 说明这里可以移动,然后后面就是绘制控件到recycle上去。         */        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {            layoutChunkResult.resetInternal();            layoutChunk(recycler, state, layoutState, layoutChunkResult);            if (layoutChunkResult.mFinished) {                break;            }            layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;            /**             * 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 (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null                    || !state.isPreLayout()) {                layoutState.mAvailable -= layoutChunkResult.mConsumed;                // we keep a separate remaining space because mAvailable is important for recycling                remainingSpace -= layoutChunkResult.mConsumed;            }            if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) {                layoutState.mScrollingOffset += layoutChunkResult.mConsumed;                if (layoutState.mAvailable < 0) {                    layoutState.mScrollingOffset += layoutState.mAvailable;                }                recycleByLayoutState(recycler, layoutState);            }            if (stopOnFocusable && layoutChunkResult.mFocusable) {                break;            }        }        if (DEBUG) {            validateChildOrder();        }        return start - layoutState.mAvailable;    }
delta 应该是增量的意思layoutState.mExtra 我是处女座的,我非要搞清楚这玩意是什么,在源码里面find一下,找到源头mLayoutState.mExtra = + upcomingOffset + mOrientationHelper.getStartAfterPadding();在垂直布局中。getTotalSpace() 就是获取整个            public int getTotalSpace() {                return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop()                        - mLayoutManager.getPaddingBottom();            }public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {        // 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.         // create layout state 创建布局状态        if (DEBUG) {            Log.d(TAG, "is pre layout:" + state.isPreLayout());        }        if (mPendingSavedState != null || mPendingScrollPosition != NO_POSITION) {            if (state.getItemCount() == 0) {                removeAndRecycleAllViews(recycler);                return;            }        }        if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {            mPendingScrollPosition = mPendingSavedState.mAnchorPosition;        }        ensureLayoutState();        mLayoutState.mRecycle = false;        // resolve layout direction        resolveShouldLayoutReverse();        mAnchorInfo.reset();        mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd;        // calculate anchor position and coordinate        updateAnchorInfoForLayout(recycler, state, mAnchorInfo);        if (DEBUG) {            Log.d(TAG, "Anchor info:" + mAnchorInfo);        }        // LLM may decide to layout items for "extra" pixels to account for scrolling target,        // caching or predictive animations.        // 当前这个类使用layout 去记录extra的像素,就是记录滚动的目标,缓存或者预测动画。        int extraForStart;        int extraForEnd;        final int extra = getExtraLayoutSpace(state); // 这里就是屏幕显示高度减去上下margin        // If the previous scroll delta was less than zero, the extra space should be laid out        // at the start. Otherwise, it should be at the end.        // 根据滚动的值,如果小于0,从上面头开始布局,否则就从尾巴开始        if (mLayoutState.mLastScrollDelta >= 0) { // mLastScrollDelta 表示最近一次的滑动            extraForEnd = extra;            extraForStart = 0;        } else {            extraForStart = extra;            extraForEnd = 0;        }        extraForStart += mOrientationHelper.getStartAfterPadding();        extraForEnd += mOrientationHelper.getEndPadding();        if (state.isPreLayout() && mPendingScrollPosition != NO_POSITION &&                mPendingScrollPositionOffset != INVALID_OFFSET) {            // if the child is visible and we are going to move it around, we should layout            // extra items in the opposite direction to make sure new items animate nicely            // instead of just fading in            // 如果有正在显示的子类,            final View existing = findViewByPosition(mPendingScrollPosition);            if (existing != null) {                final int current;                final int upcomingOffset; // 即将移动的距离                if (mShouldReverseLayout) {                // 就是getEndAfterPadding 显示的高度,减掉最下面的padding                // 与当前的控件的底部的位置的差                      current = mOrientationHelper.getEndAfterPadding() -                            mOrientationHelper.getDecoratedEnd(existing);                     // 就是当前的位置,减掉 mPendingScrollPositionOffset                    // 就是每次offset 移动的recycle的距离 缓存下来了                    upcomingOffset = current - mPendingScrollPositionOffset;                } else {                    current = mOrientationHelper.getDecoratedStart(existing)                            - mOrientationHelper.getStartAfterPadding();                    upcomingOffset = mPendingScrollPositionOffset - current;                }               // 上面的移动                if (upcomingOffset > 0) {                    extraForStart += upcomingOffset;                } else {                    extraForEnd -= upcomingOffset;                }            }        }        int startOffset;        int endOffset;        final int firstLayoutDirection;        if (mAnchorInfo.mLayoutFromEnd) {            firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL :                    LayoutState.ITEM_DIRECTION_HEAD;        } else {            firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD :                    LayoutState.ITEM_DIRECTION_TAIL;        }        onAnchorReady(recycler, state, mAnchorInfo, firstLayoutDirection);        detachAndScrapAttachedViews(recycler);        mLayoutState.mInfinite = resolveIsInfinite();        mLayoutState.mIsPreLayout = state.isPreLayout();        if (mAnchorInfo.mLayoutFromEnd) {            // fill towards start            updateLayoutStateToFillStart(mAnchorInfo);            mLayoutState.mExtra = extraForStart;            fill(recycler, mLayoutState, state, false);            startOffset = mLayoutState.mOffset;            final int firstElement = mLayoutState.mCurrentPosition;            if (mLayoutState.mAvailable > 0) {                extraForEnd += mLayoutState.mAvailable;            }            // fill towards end            updateLayoutStateToFillEnd(mAnchorInfo);            mLayoutState.mExtra = extraForEnd;            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;            fill(recycler, mLayoutState, state, false);            endOffset = mLayoutState.mOffset;            if (mLayoutState.mAvailable > 0) {                // end could not consume all. add more items towards start                extraForStart = mLayoutState.mAvailable;                updateLayoutStateToFillStart(firstElement, startOffset);                mLayoutState.mExtra = extraForStart;                fill(recycler, mLayoutState, state, false);                startOffset = mLayoutState.mOffset;            }        } else {            // fill towards end            updateLayoutStateToFillEnd(mAnchorInfo);            mLayoutState.mExtra = extraForEnd;            fill(recycler, mLayoutState, state, false);            endOffset = mLayoutState.mOffset;            final int lastElement = mLayoutState.mCurrentPosition;            if (mLayoutState.mAvailable > 0) {                extraForStart += mLayoutState.mAvailable;            }            // fill towards start            updateLayoutStateToFillStart(mAnchorInfo);            mLayoutState.mExtra = extraForStart;            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;            fill(recycler, mLayoutState, state, false);            startOffset = mLayoutState.mOffset;            if (mLayoutState.mAvailable > 0) {                extraForEnd = mLayoutState.mAvailable;                // start could not consume all it should. add more items towards end                updateLayoutStateToFillEnd(lastElement, endOffset);                mLayoutState.mExtra = extraForEnd;                fill(recycler, mLayoutState, state, false);                endOffset = mLayoutState.mOffset;            }        }        // changes may cause gaps on the UI, try to fix them.        // TODO we can probably avoid this if neither stackFromEnd/reverseLayout/RTL values have        // changed        if (getChildCount() > 0) {            // because layout from end may be changed by scroll to position            // we re-calculate it.            // find which side we should check for gaps.            if (mShouldReverseLayout ^ mStackFromEnd) {                int fixOffset = fixLayoutEndGap(endOffset, recycler, state, true);                startOffset += fixOffset;                endOffset += fixOffset;                fixOffset = fixLayoutStartGap(startOffset, recycler, state, false);                startOffset += fixOffset;                endOffset += fixOffset;            } else {                int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true);                startOffset += fixOffset;                endOffset += fixOffset;                fixOffset = fixLayoutEndGap(endOffset, recycler, state, false);                startOffset += fixOffset;                endOffset += fixOffset;            }        }        layoutForPredictiveAnimations(recycler, state, startOffset, endOffset);        if (!state.isPreLayout()) {            mPendingScrollPosition = NO_POSITION;            mPendingScrollPositionOffset = INVALID_OFFSET;            mOrientationHelper.onLayoutComplete();        }        mLastStackFromEnd = mStackFromEnd;        mPendingSavedState = null; // we don't need this anymore        if (DEBUG) {            validateChildOrder();        }    }

把人家的分析接下来写原文

0 0