So I have a problem with my app's status bar
not changing its icons to dark when I'm forcing my app into a Light Theme.
I've discovered the root of the issue, which is that the icons in the status bar
don't want to change dark when I have the SYSTEM_UI
flags LAYOUT_STABLE
and LAYOUT_FULLSCREEN
applied to the Activity
. When I remove these 2 flags, the icons in the status bar
are properly dark.
But my problem with this "solution" above is that I am using the 2 SYSTEM_UI
flags in order to have my Activity's content scroll below the status bar
, which I have made semi-transparent. I have not figured out another way to make my status bar
accept transparency and have content scroll below it other than using the 2 SYSTEM_UI
flags I currently have for this Activity
, which are, again SYSTEM_UI_FLAG_LAYOUT_STABLE
and SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
.
Does anyone have an idea as to how I can get around this issue?
Perhaps someone can show me a reliable way to have my status bar
accept transparency and have content seen scrolling below it without having to use the SYSTEM_UI
flags? That would likely solve my problem, I think...
The only code I could think would be relevant to share is this:
In my MainActivity.java, I have this set:
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);`
And in my styles.xml, I have this theme set for the Light Theme
:
<!-- Toolbar/NoActionBar variant of default Light Theme -->
<style name="AppTheme_Light_NoActionBar" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/pure_white_transparent</item>
<item name="colorPrimaryDark">@color/pure_white_transparent</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowActionBarOverlay">true</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@color/pure_white_transparent</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:navigationBarColor">@color/pure_white</item>
<item name="android:windowLightNavigationBar">true</item>
</style>
Any ideas?
Okay, so I figured it out on my own. It looks like I probably was needing to set the SYSTEM_UI
flags before calling setContentView
. Though, it could be a combination of these changes that made it work for me - I'm not entirely sure.
What I changed was using these flags before calling setContentView
:
if (lightMode) {
setTheme(R.style.AppTheme_Light_NoActionBar);
getWindow().getDecorView().setSystemUiVisibility
(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
}
lightMode
above is a SharedPreference
boolean I have set if the user has selected the Change theme
button in my app's toolbar
(works the same way for the dark mode/theme).
And finally, for my MainActivity
's theme, I have this style set in my styles.xml
(for API 27 and above - looks a bit different for lower Android APIs' styles.xml
):
<!-- Toolbar/NoActionBar variant of default Light Theme -->
<style name="AppTheme_Light_NoActionBar" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/pure_white_transparent</item>
<item name="colorPrimaryDark">@color/pure_white_transparent</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowActionBarOverlay">true</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@color/pure_white_transparent</item>
<item name="android:navigationBarColor">@color/pure_white_transparent</item>
</style>
Because it would appear to me that setting the SYSTEM_UI
flags to LAYOUT_FULLSCREEN
and LAYOUT_STABLE
makes the toolbar
sit underneath the statusbar
, I've set some appropriate padding by way of:
int statusBarHeight = getStatusBarHeight(this);
Where getStatusBarHeight
is a method used to account for the possibly varying sizes of statusbars
on different Android devices (such as the Pixel 3XL's larger statusbar
).
I got this working method from elsewhere on StackOverflow, but forgot where, so if anyone knows, feel free to link it - method below:
// Gets the StatusBar's height of the particular display.
public static int getStatusBarHeight(final Context context) {
final Resources resources = context.getResources();
final int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
return resources.getDimensionPixelSize(resourceId);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return (int) Math.ceil(24 * resources.getDisplayMetrics().density);
} else {
return (int) Math.ceil(25 * resources.getDisplayMetrics().density);
}
}
}
I've then taken the value of int statusBarHeight
and used it as the top margin for my toolbar
programatically:
// Setup the values for the toolbar's layout parameters.
CoordinatorLayout.LayoutParams toolbarParams = new CoordinatorLayout.LayoutParams(
CoordinatorLayout.LayoutParams.MATCH_PARENT,
CoordinatorLayout.LayoutParams.WRAP_CONTENT
);
toolbarParams.setMargins(0, statusBarHeight, 0, 0);
// Find, Assign, and Setup the Toolbar to take place of the Actionbar.
// Then inflate the proper menu to be used on-top of it, and set its layout parameters
// which have been set in the code above.
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.inflateMenu(R.menu.menu_main);
getSupportActionBar().setDisplayShowTitleEnabled(true);
toolbar.setLayoutParams(toolbarParams);
Finally, I've made sure my app's RecyclerView
is properly sitting below both the statusbar
and the toolbar
, which I have accomplished by setting the appropriate padding to it, also programatically:
// Get and set the values for the OffsetPadding for the RecyclerView to int.
int itemOffsetPaddingSide = (int) getResources().getDimension(R.dimen.item_offset);
int actionBarSize = (int) getResources().getDimension(R.dimen.actionbarSizeWithExtraPadding);
int itemOffsetPaddingTop = actionBarSize + statusBarHeight;
// Set all the OffsetPadding values to the RecyclerView programmatically, so that
// all the UI elements are padding properly, taking into account the devices screen
// density/size/resolution!
mRecyclerView.setPadding(itemOffsetPaddingSide, itemOffsetPaddingTop, itemOffsetPaddingSide, itemOffsetPaddingSide);
The value of int itemOffsetPaddingSide
is 4dp
, for int actionBarSize
it's 60dp
and for int itemOffsetPaddingTop
, by combining both actionBarSize
and statusBarHeight
, we get 56dp
plus however tall the statusBar
for the particular device is (because of the values retrieved and assigned to statusBarHeight
in the code further up the post).
This all allows for my RecyclerView
's content to sit comfortably below the statusbar
and toolbar
and also to be visible while scrolling underneath both of them, due to both bars
having 90% opacity
.
I've been studying Java programming and Android app development for nearly 2 years now independently, and while I'm proud of where I've come in this time, I'm also not sure this is the most elegant way to accomplish this effect in my app. But either way, it works, and if you find it useful for your app too, let me know!