Android ScrollView 子组件设置marginTop后底部内容被遮挡

问题代码

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滚动到最下面后仍然会被遮挡一部分,且滚动条也还差一点点才能滚动到最底部,如图:

image-20251029135830978

问题分析

刚开始还以为是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来实现。


Android ScrollView 子组件设置marginTop后底部内容被遮挡
http://47.109.28.82/2025/10/29/Android-ScrollView-子组件设置marginTop后底部内容被遮挡/
作者
GraftCopolymer
发布于
2025年10月29日
许可协议