文章标题
来源:互联网 发布: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
- 文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题 文章标题 文章标题 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 哪一个更快:read,fread,ifstream或者mmap
- 关于socket write error 和Software caused connection abort: recv failed 错误
- Servlet过滤器
- 打造一个万能刷新加载控件
- 在Linux服务器上配置JavaWeb项目环境(Java+Tomcat)
- 文章标题
- 金九银十北漂记第4篇:面试方欣科技
- 里程计及基于双目视觉的视觉里程计(stereo visual odometry)
- 解决walle报错:宿主机代码检出检测出错,请确认svn用户名密码无误
- java获取时间的格式
- ajax跨域解决方案以及spring mvc中的支持
- 基于MVC,实现简单的书籍信息管理,其中,模型 (M) 采用一般的JavaBean、视图 (V)采用JSP、控制 (C) 采用Servlet实现。另外,对于视图部分的JSP中,需要提供两种版本,即一般
- 【Android】Android.mk与Application.mk官网讲解
- 链表排序--冒泡法