注意其中的关键方法 measureChildWithMargins(),这个方法在 NestedScrollView 中得到了完全重写。
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { MarginLayoutParams lp = (MarginLayoutParams)child.getLayoutParams(); int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, this.getPaddingLeft() + this.getPaddingRight() + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.topMargin + lp.bottomMargin, 0); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }我们看到其中有句非常关键的代码:
int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.topMargin + lp.bottomMargin, 0);NestedScrollView 直接无视了用户设置的 MODE,直接采用了 UNSPECIFIED 做处理。经过测试发现,当我们重写 NestedScrollView 的这句代码,并且把 MODE 设置为 EXACTLY 的时候,我们得到了我们想要的效果,我已经查看 Google 的源码提交日志,并没有找到原因。
实际上,绝大多数开发之前遇到的嵌套 ListView 或者 RecylerView 只展示一行也是由于这个问题,解决方案就是重写 NestedScrollView 的 measureChildWithMargins() 或者重写 ListView 或者 RecylerView 的 onMeasure() 方法让其展示正确的高度。
我起初猜想是只有 UNSPECIFIED 才能实现滚动效果,但很遗憾并不是这样的。所以在这里抛出这个问题,希望有知情人士能一起讨论。