带有透明状态栏的 Android ScrollView 裁剪底部

Posted

技术标签:

【中文标题】带有透明状态栏的 Android ScrollView 裁剪底部【英文标题】:Android ScrollView clipped bottom with transparent statusbar 【发布时间】:2021-12-16 19:30:56 【问题描述】:

我有一个带有透明状态栏的屏幕和一个包含英雄图像和其他一些元素的 ScrollView。图像应绘制在状态栏下方。第一眼看起来一切都很好,但我注意到 ScrollView 中的最后一个视图被剪裁了。看起来 ScrollView 的高度扩展至低于屏幕高度的限制。我可以看到从屏幕下方某处开始的过度滚动效果。当我在 ScrollView 上使用 android:fitsSystemWindows="true" 时,它解决了问题,但是我没有在状态栏下绘图。

一些相关代码:

Fragment.onCreate

 WindowCompat.setDecorFitsSystemWindows(requireActivity().window, false)

应用主题

<item name="android:statusBarColor">@android:color/transparent</item>

我不确定粘贴 XML 布局是否有意义,但在伪代码中:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_>

    <LinearLayout
        android:layout_
        android:layout_
        android:orientation="vertical">

        <ImageView />

        <TextView />

        <TextView />

        <TextView />

        <TextView />
    </LinearLayout>
</ScrollView>

【问题讨论】:

【参考方案1】:

实际上,让状态栏下方的绘图按预期工作是一件很痛苦的事情。我自己使用以下几行来完成这项工作,但是现在使用标志已被标记为已弃用。

    private fun drawBelowStatusBar(window: Window) 
    if (!BitFlagsUtil.isFlagSet(
            source = window.decorView.systemUiVisibility,
            flag = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        )
    ) 
        window.decorView.systemUiVisibility = BitFlagsUtil.setFlag(
            source = window.decorView.systemUiVisibility,
            flag = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        )
    

而 util 类由这 3 个方法组成。

object BitFlagsUtil 
fun isFlagSet(source: Int, flag: Int): Boolean 
    return source and flag == flag


fun setFlag(source: Int, flag: Int): Int 
    return (source or flag)


fun unsetFlag(source: Int, flag: Int): Int 
    return (source and flag.inv())

PS。 android:fitsSystemWindows CoordinatorLayout 和 CollapsingToolbarLayout 变得棘手

【讨论】:

【参考方案2】:

WindowCompat.setDecorFitsSystemWindows(requireActivity().window, false)

From documentation: 使用这段代码可确保您的应用采用边到边,即使用显示屏的整个宽度和高度进行布局。

但这可能会使系统底部导航栏与隐藏其底部的活动重叠,在您的情况下是ScorllView的底部

解决方案:

documentation offers a solution 到使用插图的那个:

您可以通过对插图做出反应来解决重叠问题,插图指定了哪些 屏幕的某些部分与导航等系统 UI 相交 栏或状态栏。相交可能意味着简单地显示 上面的内容,但它也可以通知你的应用程序关于系统 手势也是。

这需要知道活动布局的***根ViewGroup,以便使用适当的LayoutParams。在你的情况下这是ScrollView

val root = findViewById<ScrollView>(R.id.root) // Cast that to the id of your root layout (ScrollView)
ViewCompat.setOnApplyWindowInsetsListener(root)  view, windowInsets ->
    val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())

    view.layoutParams =  (view.layoutParams as FrameLayout.LayoutParams).apply 
        leftMargin = insets.left
        bottomMargin = insets.bottom // to draw above the navigation bar
        rightMargin = insets.right
    

    // Return CONSUMED if you don't want want the window insets to keep being
    // passed down to descendant views.
    WindowInsetsCompat.CONSUMED

【讨论】:

以上是关于带有透明状态栏的 Android ScrollView 裁剪底部的主要内容,如果未能解决你的问题,请参考以下文章

带有半透明状态栏的全屏 DialogFragment

Android 5.0 半透明状态栏

带有黑色半透明状态栏的 iPhone 上的 Web 应用程序:视口高度似乎是错误的

Android 隐藏状态栏、标题栏、透明状态栏的几种方式

关于 Android 状态栏的适配总结

带有透明/模糊导航栏的 iOS 7 视图控制器布局问题