Android绘制在状态栏后面但不在导航栏后面

16

我正在寻找一种方法,使状态栏完全透明,而不是半透明,并且导航栏保持不变。

目前最接近的方法是使用标志

WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS

但这将在导航栏后面绘制。

背景是让它变得棘手的原因,它是一个微妙的渐变色,当我将状态栏颜色设置为渐变色的起点时,看起来几乎正确,但屏幕顶部有一条微妙的线。

有没有好的方法可以将活动适应窗口,仅限于顶部,但如果底部有导航栏,则不要更改它?

2个回答

27

一个好的方法是这个答案中的方法一。

To achieve a completely transparent status bar, you have to use statusBarColor, which is only available on API 21 and above. windowTranslucentStatus is available on API 19 and above, but it adds a tinted background for the status bar. However, setting windowTranslucentStatus does achieve one thing that changing statusBarColor to transparent does not: it sets the SYSTEM_UI_FLAG_LAYOUT_STABLE and SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN flags. The easiest way to get the same effect is to manually set these flags, which effectively disables the insets imposed by the Android layout system and leaves you to fend for yourself.

You call this line in your onCreate method:

getWindow().getDecorView().setSystemUiVisibility(
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

Be sure to also set the transparency in /res/values-v21/styles.xml:

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

Or set the transparency programmatically:

getWindow().setStatusBarColor(Color.TRANSPARENT);

The good side to this approach is that the same layouts and designs can also be used on API 19 by trading out the transparent status bar for the tinted translucent status bar.

<item name="android:windowTranslucentStatus">true</item>

我猜你已经尝试过这个或类似的东西。如果不起作用,请确保你的根元素是一个 FrameLayout


2
默认使用android.support.design.widget.CoordinatorLayout而不是Frame Layout。这样就解决了问题,谢谢。 - DR Haus

5

免责声明:此答案是补充答案,因为已接受的答案在API级别30之前是有效的,因为系统可见性标志在API级别30之后已被弃用。

setDecorFitsSystemWindows()会将边缘到边缘应用于您的应用程序(即,应用程序将扩展其内容以跨越整个屏幕,覆盖系统状态和导航栏)。

可以在活动中实现此功能:

WindowCompat.setDecorFitsSystemWindows(window, false)

现在还剩下一个问题需要解决,就是避免与系统导航栏重叠:

根据文档:

您可以通过对视图控制器的 insets 进行反应来处理重叠问题,insets 可以指定屏幕的哪些部分与系统 UI(如导航栏或状态栏)相交。相交可能仅意味着显示在内容上方,但也可以告知您的应用程序有关系统手势的信息。

所以,我们需要处理 API 级别 30+ 的 insets,以避免应用程序与底部导航栏之间的重叠:

/*
*  Making the Navigation system bar not overlapping with the activity
*/
if (Build.VERSION.SDK_INT >= 30) {

    // Root ViewGroup of my activity
    val root = findViewById<ConstraintLayout>(R.id.root)

    ViewCompat.setOnApplyWindowInsetsListener(root) { view, windowInsets ->

        val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())

        // Apply the insets as a margin to the view. Here the system is setting
        // only the bottom, left, and right dimensions, but apply whichever insets are
        // appropriate to your layout. You can also update the view padding
        // if that's more appropriate.

        view.layoutParams =  (view.layoutParams as FrameLayout.LayoutParams).apply {
            leftMargin = insets.left
            bottomMargin = insets.bottom
            rightMargin = insets.right
        }

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

}

查看文档获取更多信息。

额外说明:

  • 如果您要在较低的API级别上使用<item name="android:windowTranslucentStatus">true</item>,则必须在API-30中覆盖主题/样式;即需要一个默认样式的res\values-v30\themes.xml(当然不包括windowTranslucentStatus

有没有办法使用WindowCompat的新方法仅在状态栏后面绘制?目前这会使应用程序在状态栏和导航栏后面绘制,就像您所提到的那样。 - vepzfe
@vepzfe,您能否在回答中添加一段代码 ViewCompat.setOnApplyWindowInsetsListener,这样它就可以绘制在底部导航栏上方...同时确保您没有添加 topMargin = insets.top,以便它不会绘制在状态栏上。 - Zain
1
请查看此答案:https://dev59.com/P1EG5IYBdhLWcg3wTJjV;其中有更详细的内容。 - Zain
1
谢谢提供链接。简而言之,避免在导航栏后面绘制的唯一方法是使用插图并设置底部边距,因为没有办法告诉系统不要在应用程序那里绘制。 - vepzfe
1
我明白了。旧的弃用代码仍然有效。我之所以在寻找系统自动执行此操作的方法,是因为我特定活动的布局非常复杂,有多个底部工作表,因此设置底部边距不起作用,并且需要在布局中进行大量更改。因此,在我找到更好的解决方案之前,我将使用旧的弃用标志。我尝试将底部边距设置为decor view、decorView.rootView甚至窗口,但这些都没有起作用。 - vepzfe
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接