为什么会失效呢,该怎样去分析。
这一块还得看NestedScrollView的代码及RecyclerView的复用原理。
NestedScrollView
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!mFillViewport) {
return;
}
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode == MeasureSpec.UNSPECIFIED) {
return;
}
if (getChildCount() > 0) {
View child = getChildAt(0);
final NestedScrollView.LayoutParams lp = (LayoutParams) child.getLayoutParams();
int childSize = child.getMeasuredHeight();
int parentSpace = getMeasuredHeight()
- getPaddingTop()
- getPaddingBottom()
- lp.topMargin
- lp.bottomMargin;
//如果孩子高度小于父布局的高度,则对孩子的布局进行精确测量
if (childSize < parentSpace) {
int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin,
lp.width);
int childHeightMeasureSpec =
MeasureSpec.makeMeasureSpec(parentSpace, MeasureSpec.EXACTLY);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
看下super.onMeasure(widthMeasureSpec, heightMeasureSpec)
方法,NestedScrollView继承自FrameLayout,我们看FrameLayout的onMeasure方法。
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
measureChildWithMargins
这个方法是个关键,
NestedScrollView重写了measureChild
、measureChildWithMargins
两个方法。
@Override
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
ViewGroup.LayoutParams lp = child.getLayoutParams();
int childWidthMeasureSpec;
int childHeightMeasureSpec;
childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft()
+ getPaddingRight(), lp.width);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
@Override
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
在这两个方法中,测量模式设置为MeasureSpec.UNSPECIFIED
。当测量模式为MeasureSpec.UNSPECIFIED
时,父容器没有对子View有任何限制,子View可以任意取尺寸,即大小有子View决定。
RecyclerView
来分析下RecyclerView中的代码,以LinearLayoutManager为例,RecyclerView是将绘制流程交给LayoutManager处理。
LinearLayoutManager#onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)
...
mLayoutState.mInfinite = resolveIsInfinite();
....
fill(recycler, mLayoutState, state, false);
我们分析下resolveIsInfinite()方法,
当父View是View.MeasureSpec.UNSPECIFIED的时候,则返回为true。
boolean resolveIsInfinite() {
return mOrientationHelper.getMode() == View.MeasureSpec.UNSPECIFIED
&& mOrientationHelper.getEnd() == 0;
}
mLayoutState.mInfinite
的值为true,我们定位
fill
的方法。
//这里是关键,因为layoutState.mInfinite为true,因此会将getItemCount()得到的count数据都加载出来
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.SCROLLING_OFFSET_NaN) {
layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
if (layoutState.mAvailable < 0) {
layoutState.mScrollingOffset += layoutState.mAvailable;
}
recycleByLayoutState(recycler, layoutState);
}
if (stopOnFocusable && layoutChunkResult.mFocusable) {
break;
}
}
layoutChunk方法
//得到View,这里会走相关的缓存策略
View view = layoutState.next(recycler);
...
//添加View
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);
}
}
总结
- 首先NestedScrollView对
measureChild
、measureChildWithMargins
两个方法进行重写,测量模式为MeasureSpec.UNSPECIFIED
,当测量模式为MeasureSpec.UNSPECIFIED
时,父容器没有对子View有任何限制,子View可以任意取尺寸,即大小有子View决定。 - RecyclerView中onMeasure时,调用LayoutManager的onLayoutChildren方法中,将
mLayoutState.mInfinite
设置为true。 - LayoutManager调用fill方法绘制时,会根据layoutState.mInfinite来判断,如果为true的话,会将会将
getItemCount()
得到的count数据都加载出来。