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)
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
}
}