在使用Lint扫描工程时,看到这个提示。 Google推荐将ScrollView的子View高度设置为wrap_content, 但实际业务开发时可能根节点是LinearLayout(layout_height="match_parent"), 然后发现屏幕显不下就包了一层ScrollView。 运行看到ScrollView能正常上下滑动,就没改LinearLayout的layout_height属性。
为什么ScrollView仍然能上下滑动呢??? 按照安卓View的测量方式LinearLayout应该跟ScrollView的高度相同。 去源码里找答案:ScrollView重写了ViewGroup的measureChildWithMargins方法, 该方法会在onMeasure里调用。
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 usedTotal = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin +
heightUsed;
final int childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - usedTotal),
MeasureSpec.UNSPECIFIED);
//设置child高度的测量方式为UNSPECIFIED, 这也是ScrollView子View高度参数无效的原因。
//UPSPECIFIED表示child高度由自己决定,不受父容器的限制
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
核心是设置child高度测量方式为UNSPECIFIED, 这就是为什么LinearLayout设置高度为match_parent仍然能够正常滑动的原因。 后面再讲为什么ScrollView要篡改子View高度的测量方式为UNSPECIFIED。
ScrollView和子View高度可以设置为wrap_content或者match_parent(与固定值高度情况相同)、 再考虑子View高度大于/小于ScrollView的高度,排列组合有8种情况。 上面说到给ScrollView的子View设置高度参数无效, 所以剩下4种情况。
第一种情况:ScrollView高度是match_parent或固定值且子View高度小于ScrollView, 则子View高度是实际需要的高度。 如果需要子View高度等于父容器ScrollView, 则需要添加子View即LinearLayout属性android:fillViewPort="true"。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//对应ScrollView的android:fillViewPort属性,默认值false
if (!mFillViewport) {
return;
}
//设置android:fillViewPort="true“后才会执行下面的代码
final int heightMode