Android透明状态栏下ScrollView底部裁剪与fitsSystemWindows冲突问题求解
问题根源
你调用WindowCompat.setDecorFitsSystemWindows(window, false)后,Activity的内容区域会延伸覆盖状态栏、导航栏的整个屏幕区域,此时ScrollView高度设为match_parent时会直接占满整个物理屏幕高度,底部内容会被系统导航栏遮挡,就会出现底部裁剪、滚动触发位置异常的问题。
如果直接给ScrollView设置android:fitsSystemWindows="true",系统会自动给ScrollView添加顶部padding(高度等于状态栏高度),这就会导致顶部的ImageView没法延伸到状态栏下方,和需求冲突。
解决方案
不要依赖系统默认的fitsSystemWindows逻辑,手动处理窗口边距,保留顶部状态栏的穿透效果,同时给内容底部添加对应导航栏高度的padding:
步骤1:保留原有基础配置
原来的WindowCompat.setDecorFitsSystemWindows配置、状态栏透明主题配置不需要修改,xml布局也不需要加fitsSystemWindows属性。
步骤2:给ScrollView添加窗口边距监听
在Fragment的onViewCreated生命周期中添加如下逻辑,手动给ScrollView内部的根LinearLayout设置底部padding,数值等于系统导航栏的高度:
ViewCompat.setOnApplyWindowInsetsListener(scrollView) { view, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) // 内部LinearLayout只加底部padding,顶部不加,保证首图可以延伸到状态栏下方 val innerLayout = view.getChildAt(0) as LinearLayout innerLayout.setPadding( innerLayout.paddingLeft, innerLayout.paddingTop, innerLayout.paddingRight, // 原有padding加上导航栏高度 innerLayout.paddingBottom + systemBars.bottom ) // 返回未消费的insets,不影响其他视图的边距计算 insets }
Java版本代码参考:
ViewCompat.setOnApplyWindowInsetsListener(scrollView, (view, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); LinearLayout innerLayout = (LinearLayout) view.getChildAt(0); innerLayout.setPadding( innerLayout.getPaddingLeft(), innerLayout.getPaddingTop(), innerLayout.getPaddingRight(), innerLayout.getPaddingBottom() + systemBars.bottom ); return insets; });
补充说明
该逻辑会自动适配手势导航、三大金刚键导航等不同模式下的导航栏高度,无需额外做机型适配。如果页面顶部有自定义标题栏需要避开状态栏,也可以在同一个监听逻辑里单独给标题栏添加顶部padding,不会影响ImageView的状态栏穿透效果。
内容的提问来源于stack exchange,提问作者Billda

