Android Draw 在状态栏后面但不在导航栏后面

Posted

技术标签:

【中文标题】Android Draw 在状态栏后面但不在导航栏后面【英文标题】:Android Draw behind Status bar but not behind the navigation bar 【发布时间】:2016-01-26 02:04:02 【问题描述】:

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

我能得到的最接近的是使用标志

WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS

但这也会在导航栏后面绘制。

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

有没有一种好方法可以让 Activity 适应窗口,只在顶部,但如果底部有导航栏,就不要管它了吗?

【问题讨论】:

【参考方案1】:

一个好的方法是来自this answer 的方法一。

要实现完全透明的状态栏,你必须使用 statusBarColor,仅适用于 API 21 及更高版本。 windowTranslucentStatus 在 API 19 及更高版本中可用,但它为状态栏添加了有色背景。但是,设置 windowTranslucentStatus 确实实现了将 statusBarColor 更改为透明的一件事:它设置了 SYSTEM_UI_FLAG_LAYOUT_STABLE 和 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 标志。获得相同效果的最简单方法是手动设置这些 标志,它有效地禁用了 android 强加的插图 布局系统,让您自生自灭。

您在 onCreate 方法中调用此行:

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

请务必在 /res/values-v21/styles.xml 中设置透明度:

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

或以编程方式设置透明度:

getWindow().setStatusBarColor(Color.TRANSPARENT);

这种方法的好处是相同的布局和设计 也可以通过交换透明状态栏在 API 19 上使用 用于有色半透明状态栏。

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

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

【讨论】:

默认为 android.support.design.widget.CoordinatorLayout 而不是框架布局。成功了,谢谢【参考方案2】:

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

setDecorFitsSystemWindows() 为您的应用实现edge-to-edge(即应用将扩展其内容以扩展到整个屏幕以覆盖系统状态栏和导航栏)

这可以在活动中实现:

WindowCompat.setDecorFitsSystemWindows(window, false)

现在剩下的就是避免与系统导航栏重叠:

As per documentation:

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

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

/*
*  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
    


Check documentation 了解更多信息。

加分:

如果您打算将&lt;item name="android:windowTranslucentStatus"&gt;true&lt;/item&gt; 用于较低的API 级别,那么您必须覆盖API-30 中的主题/样式;即有一个默认样式的res\values-v30\themes.xml(当然没有windowTranslucentStatus

【讨论】:

有没有办法使用 WindowCompat 的这个新方法只在状态栏后面绘制?正如您所提到的,这目前使应用程序在状态栏和导航栏后面绘制。 @vepzfe 你能在答案ViewCompat.setOnApplyWindowInsetsListener 中添加这段代码吗,这将使它在底部导航栏上方绘制......还要确保你没有添加`topMargin = insets .top` 使其不会在状态栏上绘制 也请查看this answer;还有更详细的 感谢您的链接。所以简而言之,避免在导航栏后面绘制的唯一方法是使用插图并设置底部边距,因为没有办法告诉系统不要在那里绘制应用程序。 我明白了。我现在知道了。诚然,旧的弃用代码仍然有效。我之所以要为系统寻找一种方法,是因为我的特定活动的布局非常复杂,有多个底部工作表,因此设置底部边距不起作用,并且需要在布局中进行大量更改。因此,直到我能找到更好的解决方案,使用旧的弃用标志。我尝试为装饰视图和 decorView.rootView 甚至使用 window.attributes 的窗口设置下边距,但这些都不起作用。

以上是关于Android Draw 在状态栏后面但不在导航栏后面的主要内容,如果未能解决你的问题,请参考以下文章

如何让 Flutter 应用在​​ android 导航栏后面绘制并使导航栏完全透明?

android关掉导航栏后怎么

Android 全屏片段不显示导航和状态栏后面的元素

在状态和导航栏后面显示内容

状态栏后面的布局 - Android Lollipop

系统覆盖不覆盖状态栏和导航栏 Android