Android Draw behind Status bar but not behind the navigation bar - android

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?

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.

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)

Related

How to implement system bars insets with edge-to-edge gesture navigation?

I'm trying to add the edge-to-edge stuff for the gesture navigation bar to the Tip Time app from Google. I added the transparent navigationBarColor XML tag to themes.xml as well as the following code to the onCreate() function of MainActivity.kt:
This was directly copy-pasted from the documentation. Android Studio says that "it cannot find a parameter with this name" for each of the three margins. I noticed that changing the parenthesis right after <ViewGroup.MarginLayoutParams> to curly braces fixes the compiler error. Maybe the documentation is just wrong?
Anyways, even after fixing that, the app still doesn't look right:
As you can see, the entire view gets shifted up slightly and the "Cost of Service" TextView is partially cut-off by the app bar. What would I need to change to implement the system/navigation bar insets for edge-to-edge content so the UI looks nice? Also, as a side-question, how can I change the dark blue color of the system status bar to match the color of the app bar so that they look blended?
As per documentation for edge to edge contents:
Draw behind the status bar if it makes sense for your content and
layout, such as in the case of full-width imagery. To do this, use
APIs such as AppBarLayout, which defines an app bar pinned to the top
of the screen.
So, while handing the window insets (especially the top one), you can't use the default ActionBar, instead you need to customize that with AppBarLayout and ToolBar, and to make it act as the ActionBar, use setSupportActionBar(), and a NoActionBar app theme; it'd be <style name="Theme.TipTime" parent="Theme.MaterialComponents.DayNight.NoActionBar"> in the shared repo.
the entire view gets shifted up slightly and the "Cost of Service" text field is partially cut-off by the app bar.
The reason that the sample uses the default ActionBar instead of a customized one; when it comes to handle the top window insets, it won't affect the default ActionBar; notice that you pass in the activity's root layout to setOnApplyWindowInsetsListener callback, and as the ActionBar is not a part of the activity, it won't be affected. Therefore the activity is shifted up behind the ActionBar when the top inset is removed. So, to solve this you have either to totally remove the default ActionBar or to use a custom toolbar instead.
Also, as a side-question, how can I change the dark blue color of the system status bar to match the color of the app bar so that they look blended?
Use a transparent status bar color in the app's theme:
<item name="android:statusBarColor" tools:targetApi="l">#android:color/transparent</item>

Switch Status Bar Text Color / setAppearanceLightStatusBars of WindowController is not working as expected

I am using two different backgrounds for my app depending on which screen is active. In general the status bar is transparent and the background should lay behind it. Depending on the background I need the color of my status bar text to be either light or dark.
So far I used this theme attributes to get the status bar transparent (well its translucent really, but if anyone knows how to get it transparent it would be appreciated)
<item name="android:windowTranslucentStatus">true</item>
<item name="android:statusBarColor">#android:color/transparent</item>
I am using Jetpack navigation to navigate and pass arguments to the destination whenever the background should switch like this
mNavController.addOnDestinationChangedListener((controller, destination, arguments) -> {
if (arguments != null) {
setUseSecondaryBackground(arguments.getBoolean("bg_line_style", false));
}
});
This works really good. Now the part that does not work is the switching of the status bar text color:
private void setUseSecondaryBackground(boolean useLineBackground) {
if (useLineBackground) {
//Stuff to switch backgrounds
windowController.setAppearanceLightStatusBars(false); //Works fine
} else {
//Stuff to switch backgrounds
//Does not work!!! If enabled the whole background is shifted downward by the status bar hight - see pictures
windowController.setAppearanceLightStatusBars(true);
}
}
This is the used controller, created in onCreate() of my MainActivity
windowController = new WindowInsetsControllerCompat(this.getWindow(), this.getWindow().getDecorView());
Here you see that the status bar is green because the background of the mainActivity lays behind the transculent status bar. windowController.setAppearanceLightStatusBars(true); is commented for this version
Here you see that the status bar is white, the text is white (because it is still the green background) but it got shifted down and replaced by some kind of default white status bar background. Look at where the battery icon is now. windowController.setAppearanceLightStatusBars(true); Is not commented and was executed somewhere along the road
UPDATE:
Following this I was able to get the desired result on my testing device running Android 10.
On the Emulator Running API 30 I now have one additional Problem:
WindowCompat.setDecorFitsSystemWindows(window, false);
lets us draw behind the status bar but also behind the navigation bar. Now the status bar works as intended but the navigation bar hides part of the UI. I need the navigation bar in its old state and not to be drawn under. Any ideas?
For completeness
compileSdkVersion 31
minSdkVersion 23
targetSdkVersion 31
Following this I was able to get the desired result on my testing
device running Android 10.
On the Emulator Running API 30 I now have one additional Problem:
WindowCompat.setDecorFitsSystemWindows(window, false);
lets us draw behind the status bar but also behind the navigation bar.
Now the status bar works as intended but the navigation bar hides part
of the UI. I need the navigation bar in its old state and not to be
drawn under. Any ideas?
This is right, because setDecorFitsSystemWindows() implements edge-to-edge to your app (i.e. the app will cover both the system status & navigation bars)
But, 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 System bars insets for API level 30+ to avoid your app overlapping with the navigation bar:
This requires the top root ViewGroup of your activity layout, and accordingly you need to cast the LayoutParams appropriately.
Here I am using FrameLayout.LayoutParams:
/*
* 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
}
}

Android - Different Status Bar issues when changing fragments

I am facing a rather annoying issue on Android.
I have an activity which has 3 fragments inside, all fragments fill the screen. The designer was very specific about the look of the statusbar on the devices and this is what he requested:
First Fragment: Transparent Status Bar with White Text and a background image that also falls behind the status bar.
Second Fragment: Transparent Status Bar with Dark Text and a background image that also falls behind the status bar.
Third Fragment: Status Bar with Custom Color and Dark Text that doesn't let content get beneath it.
How on earth do I do this, considering that they all must be fragments in a single activity and I've played with flags like WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, windowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR, decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN, decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
but no combination results in a good behavior. Literally I can't get it to have the requested look. Sometimes, one fragment has a good status bar, but if I go back, the status bar stops working properly on the previous fragment and so on.
Can somebody please teach me how can I achieve this.
P.S. I can't believe Android is such a damn disappointing mess of an operating system sometimes. Handling the look of the status bar should have been an one-liner, easy to use, just like on iOS....
Just use it in every fragment
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.BLUE);
}
I'm doing it in that way:
window.statusBarColor = ContextCompat.getColor(this, R.color.white)
remember that you have to change the color of icons at status bar also ; )
window.decorView.windowInsetsController?.setSystemBarsAppearance(APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS)
I'm using navigation component and at Activity I've got a navigation change listener who provide me current fragment's id.

How to set completely transparent navigation bar (NOT TRANSLUCENT) and keep receiving window inset updates

The image below is basically my goal, a completely transparent navigation bar (NOT TRANSLUCENT) and the content behind it keeps getting inset updates so it draws behind it, but does not get any UI covered by the navigation controls.
So far the only way possible to achieve the result in the image above is to set the flag FLAG_LAYOUT_NO_LIMITS to the activity. The problem is when that is done, the app no longer receives any insets whatsoever, with the exception of Gesture insets, if gesture navigation is enabled.
I've tried all the combinations I could think of, but so far I am completely unable to achieve 100% transparent navigation bar + window insets.
I assume that the reason why the navigation bar is not translucent when setting that flag is because the system window insets are now 0, as such the translucent area shrinks to 0 height, leaving the navigation controls without background.
I also tried setting
<item name="android:navigationBarColor">#android:color/transparent</item>
However, all this does is leave the navigation bar fully white and the content does not display behind it.
And I also had the following enabled and disabled for all the tests I conducted
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
I've researched high and above the Window Insets nightmare and there's one way to find the window insets via getWindow().getDecorView().getRootWindowInsets(), but then I will be falling to the problem of adding insets when they aren't needed (multi-window mode, for example) and I still won't get window inset updates, such as when the keyboard is opened.
I've watched ( #chris-banes ) Chris Banes' "Becoming a master window fitter", however this specific scenario is never covered.
Is this impossible? We can achieve this result for the status bar, but we will never be able to do the same for the navigation bar? If not, then how can this be achieved?
All I want is a 100% transparent (NOT TRANSLUCENT) navigation bar and the app keeps receiving the window insets with the expected values instead of 0 for all the values.
Chris Banes also has a more recent blog post, going through exactly what you are asking for:
https://medium.com/androiddevelopers/windowinsets-listeners-to-layouts-8f9ccc8fa4d1
In short, set the following system ui flags on the root view:
rootView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
To get the following result:
Then you can update the padding or margin of your view to make it available for touch events by adding an onApplyWindowInsetsListener:
bottomNav.setOnApplyWindowInsetsListener { view, insets ->
view.updatePadding(bottom = insets.systemWindowInsetBottom)
insets
}
For the full solution, I suggest you read the whole post, it's a good read and has helped me a lot.

Overlay action bar with a translucent status bar

I'm trying to achieve the effect shown here: http://www.youtube.com/watch?v=6QHkv-bSlds&t=15m48s by Nick and the boys. I can get the action bar to be overlayed, but cannot figure out how to extend this to the status bar. I would also like to know how they managed the transparent black background behind the navigation bar (but this isn't as crucial).
Any help / advice would be greatly appreciated as I currently have no idea how this is done (and am starting to worry it may just be an image rather than an actual implementation).
Edit: i know how to make the bars fully transparent (thats the easy part)! I dont know how to extend the actionbar background to appear behind the now transluscent status bar
I had the same question and found this library: https://github.com/jgilfelt/SystemBarTint
Have a look at line 300 in:
https://github.com/jgilfelt/SystemBarTint/blob/master/library/src/com/readystatesoftware/systembartint/SystemBarTintManager.java
setupStatusBarView() Adds a view to the window's decor. This allows you to later set a color/drawable to this view.
If using the SystemBarTint library, the following will allow you to force the status bar to a specified color, which you can then match to your action bar's background:
SystemBarTintManager tintManager = new SystemBarTintManager(this);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintColor(Color.parseColor("#DD000000"));
In this case you would set your action bar's background to: #DD000000
As described in the Android 4.4 APIs:
You can now make the system bars partially translucent with new themes, Theme.Holo.NoActionBar.TranslucentDecor and Theme.Holo.Light.NoActionBar.TranslucentDecor. By enabling translucent system bars, your layout will fill the area behind the system bars, so you must also enable fitsSystemWindows for the portion of your layout that should not be covered by the system bars.
If you're creating a custom theme, set one of these themes as the parent theme or include the windowTranslucentNavigation and windowTranslucentStatus style properties in your theme.
In your case (where you want the ActionBar), the second option - including the two new properties into your theme - will give you a translucent Navigation and Status bar.
They are using the new Translucent system bars feature (on Android 4.4 and up).
Translucent system bars
You can now make the system bars partially translucent with new
themes, Theme.Holo.NoActionBar.TranslucentDecor and
Theme.Holo.Light.NoActionBar.TranslucentDecor. By enabling translucent
system bars, your layout will fill the area behind the system bars, so
you must also enable fitsSystemWindows for the portion of your layout
that should not be covered by the system bars.
If you're creating a custom theme, set one of these themes as the
parent theme or include the windowTranslucentNavigation and
windowTranslucentStatus style properties in your theme.

Categories

Resources