此为recyclerView 布局中最终落脚方法了。先看整个源码。
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
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.
result.mFinished = true;
return;
}
LayoutParams params = (LayoutParams) view.getLayoutParams();
if (layoutState.mScrapList == null) {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addView(view);
} else {
addView(view, 0);
}
} else {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addDisappearingView(view);
} else {
addDisappearingView(view, 0);
}
}
measureChildWithMargins(view, 0, 0);
result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
int left, top, right, bottom;
if (mOrientation == VERTICAL) {
if (isLayoutRTL()) {
right = getWidth() - getPaddingRight();
left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
} else {
left = getPaddingLeft();
right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
}
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
bottom = layoutState.mOffset;
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.
layoutDecoratedWithMargins(view, left, top, right, bottom);
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();
}
*************************************************************************************************************************************************
开始源码分析:
View view = layoutState.next(recycler); 从recycler 取出View对象。
/**
* Gets the view for the next element that we should layout.
获取接下来需要布局的View。同时更新目前的item的索引。
* Also updates current item index to the next item, based on {@link #mItemDirection}
*
* @return The next element that we should layout.
*/
如果获取的view为空。那么直接返回了。意味着recyview已经布局完毕。这个地方是结束标志,重要。接下来就是正常的布局View的逻辑。
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.
result.mFinished = true;
return;
}
实际情况,布局是测量的后一步。布局之前必须测量。子View还没添加进入ViewGroup,也没有测量。下面的步骤就是。 LayoutParams params = (LayoutParams) view.getLayoutParams();
获取View的布局参数。如果废弃的mScrapList 为空的逻辑。第一次布局为空。
if (layoutState.mScrapList == null) {
// 判断布局的方向,分为从顶部和从底部两个方向。
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addView(view);
//将View加入child 里面。暂时不分析里面的细节。
} else {
addView(view, 0);
}
} else {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addDisappearingView(view);
} else {
addDisappearingView(view, 0);
}
}
//对View进行测量。实际上大家发现,这个过程把加入,测量,布局都融合一块了。细节暂时不分析。
measureChildWithMargins(view, 0, 0);
//测量完毕,分析此View 消耗了多少的空间。此处如果是纵向的布局,会分析此View占据的高度。加上padingTop和paddingBottom
result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
int left, top, right, bottom;
if (mOrientation == VERTICAL) {
if (isLayoutRTL()) {
//垂直布局,但是方向是从右往做开始布局。
right = getWidth() - getPaddingRight();
left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
} else {
left = getPaddingLeft();
right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
}
/./计算view布局的bottom和top值,那么需要检查布局的顺序是从上到下,还是从下到上。
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
bottom = layoutState.mOffset;
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;
}
}
//开始对View进行layout。这一步之后View就布局彻底结束。
layoutDecoratedWithMargins(view, left, top, right, bottom);
// Consume the available space if the view is not removed OR changed
//设置mIgnoreConsumed的值。此值最后啥用得返回到上层调用。后续章节分析。
if (params.isItemRemoved() || params.isItemChanged()) {
result.mIgnoreConsumed = true;
}
// 设置是否聚焦。因为recycler的布局可以布局到聚焦点结束这么个变量。所以此处传出去。
result.mFocusable = view.isFocusable();
此方法整体分析完毕了。后续会把recyview的布局和事件传递整体分析出来。