问题代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".views.AddCEItemActivity">
<com.google.android.material.appbar.MaterialToolbar android:id="@+id/appbar" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:title="添加综测项目"/>
<ScrollView android:id="@+id/scroll_view" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/appbar" app:layout_constraintBottom_toBottomOf="parent">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:orientation="vertical">
<Button android:id="@+id/btn_submit" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="提交"/> </LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
|
上述代码在实际运行时,LinearLayout最底部的Button即使在ScrollView滚动到最下面后仍然会被遮挡一部分,且滚动条也还差一点点才能滚动到最底部,如图:

问题分析
刚开始还以为是Activity中的enableEdgeToEdge()这个函数让最底部的内容被系统的导航条给遮挡了,但是在代码中还同时使用了:
1 2 3 4 5
| ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) insets }
|
设置了整个View的padding,所以应该不是这个函数的问题。
经过一番排查,最终发现是ScrollView在计算可滚动范围时,没有计算子组件的margin,查看ScrollView中的源码:
1 2 3 4 5 6 7 8 9
| private int getScrollRange() { int scrollRange = 0; if (getChildCount() > 0) { View child = getChildAt(0); scrollRange = Math.max(0, child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop)); } return scrollRange; }
|
可以看到只是调用了child.getHeight(),而这个方法是不包括marginTop以及marginBottom的,所以无论是否设置了子组件的marginTop,最终计算出来滚动范围都是一样的。这就导致了子组件真正的内容被marginTop往下挤到了滚动不到的地方,出现了上述被遮挡的结果。
解决方案
我们的目的只是在子组件的上方加上一个间距与页面顶部分隔一段距离,既然设置子组件的marginTop行不通,我们还可以设置子组件的paddingTop或者ScrollView本身的paddingTop来实现。