16

I'm looking into a way where the status bar is completely transparent, not translucent and where the Navigation Bar is left untouched.

Closest I can get is to use the flag

WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS

but this draws behind the Navigation Bar as well.

The background is what is making this tricky, it is a subtle gradient, and when I set the status bar color to the start of the gradient, it looks almost right, but has a subtle line across the top of the screen.

Is there a good way to fit the activity to the window, only at the top but if there is a navigation bar at the bottom, leave it alone?

Zain
  • 37,492
  • 7
  • 60
  • 84
DR Haus
  • 652
  • 1
  • 7
  • 12

2 Answers2

27

A good approach is Method One from this answer.

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>

I'm guessing you've tried this or something similar. If that's not working, make sure your root element is a FrameLayout.

Community
  • 1
  • 1
Ben Kane
  • 9,331
  • 6
  • 36
  • 58
  • 2
    Defaulted to android.support.design.widget.CoordinatorLayout instead of Frame Layout. That did the trick, thanks – DR Haus Oct 26 '15 at 20:36
5

Disclaimer: This is complementary answer as the accepted answer is valid without deprecation > till API level 30 because the system visibility flags are deprecated as of API level 30.

setDecorFitsSystemWindows() implements edge-to-edge to your app (i.e. the app will be expanding its content to extend across the entire screen to cover both the system status & navigation bars)

And this can be implemented in the activity with:

WindowCompat.setDecorFitsSystemWindows(window, false)

Now the remaining part is to avoid the overlapping with the system navigation bar:

As per documentation:

You can address overlaps by reacting to insets, which specify which parts of the screen intersect with system UI such as the navigation bar or the status bar. Intersecting can mean simply being displayed above the content, but it can also inform your app about system gestures, too.

So, We need to handle the insets for API level 30+ to avoid the overlapping between the app and the bottom navigation bar:

/*
*  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 for more info.

Extra cent:

  • If you're going to use <item name="android:windowTranslucentStatus">true</item> for lower API levels, then you have to override the themes/styles in API-30; i.e. to have a res\values-v30\themes.xml with the default style (of course without windowTranslucentStatus)
Zain
  • 37,492
  • 7
  • 60
  • 84
  • Is there any way to draw only behind the status bar using this new method from WindowCompat? This currently makes the app draw behind both status bar and nav bar as you mentioned. – vepzfe Nov 13 '21 at 11:49
  • @vepzfe Can you add the piece of code in the answer `ViewCompat.setOnApplyWindowInsetsListener`, this will make it draws above the bottom navigation bar... also make sure that you didn't add ` topMargin = insets.top` so that it doesn't draw on the status bar – Zain Nov 13 '21 at 12:32
  • 1
    Also please check [this answer](https://stackoverflow.com/questions/68451704/transparent-status-bar-with-visible-navigation-bar#68492550); there are more in detail – Zain Nov 13 '21 at 12:34
  • 1
    Thanks for the link. So in short, the only way to avoid drawing behind the navigation bar is to use the insets and set a bottom margin since there is no way to tell the system to not draw the app there. – vepzfe Nov 15 '21 at 08:59
  • @vepzfe Yes this is right if you are using `WindowCompat.setDecorFitsSystemWindows(window, false)`, because this line of code triggers `setOnApplyWindowInsetsListener` callback where it can measure the height of the system bottom navigation bar using `insets` - This can be used for newer APIs (API-21+, but I do recommend to use it from API-30+, because the `setOnApplyWindowInsetsListener` is automatically removed/consumed after the first call in API30+; but I think you can remove it manually for APIs below 30.. But generally, for old APIs window flags still works in the accepted answer. – Zain Nov 15 '21 at 10:34
  • 1
    I see. I understand now. True, the old deprecated code still works. The reason why I was looking for a way for the system to do it is because my layouts for a particular activity are pretty complex with multiple bottom sheets so setting a bottom margin wont work and would need changing a lot in the layout. So until I can figure out a better solution, using the old deprecated flags. I tried setting a bottom margin to the decor view and the decorView.rootView and even the window using window.attributes but none of those worked. – vepzfe Nov 15 '21 at 11:56
  • I love you man you saved my life – Mahmoud Aly Dec 30 '21 at 06:25