ScrollView是继承FrameLayout,其对子view的测量比较怪异,因为其重写了FrameLayout中的几个测量方法
@Override
//scrollView重写的onMeasure方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//调用frameLayout的onMeasure方法进行测量,该方法会调用下面分析的measureChildWithMargins方法
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//这个变量由scrollView的 android:fillViewport属性决定,默认是false
if (!mFillViewport) {
return;
}
//如果设置了android:fillViewport="true"会调用下面代码
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//一般不等于MeasureSpec.UNSPECIFIED
if (heightMode == MeasureSpec.UNSPECIFIED) {
return;
}
if (getChildCount() > 0) {
//对唯一的子view进行处理
final View child = getChildAt(0);
//获取自身高度
int height = getMeasuredHeight();
if (child.getMeasuredHeight() < height) {
//如果测量出的子view高度小于自己,则执行下列代码,重新测量,其实就是当子view高度小于自身高度时,将子view高度设置为等于自身高度
final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
height -= mPaddingTop;
height -= mPaddingBottom;
int childHeightMeasureSpec =
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
另外ScrollView重写了ViewGroup的measureChildWithMargins方法(同时也重写了measureChild方法),这个方法会在super.onMeasure方法里面调用
@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,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
//这里是其重写关键,改变了高度的测量规格
final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED);
//然后才进行子view的测量,由于设置了MeasureSpec.UNSPECIFIED规格,会影响子view的测量
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
总结
- scrollView的子view高度属性android:layout_height设置无效,无论你设置为什么,其测量时会在measureChildWithMargins中将其变为MeasureSpec.UNSPECIFIED
- 当设置 android:fillViewport属性为true时,如果测量的子view的高度小于scrollview自身的高度,则最终将子view设置为scrollview自身高度