Android M Light and Dark status bar programmatically - how to make it dark again?
AndroidUser InterfaceAndroid 6.0-MarshmallowAndroid StatusbarAndroid Problem Overview
In the Android M we have ability to make status bar icons dark. To do that we can specify attribute in the theme's xml:
<item name="android:windowLightStatusBar">true</item>
OR we cat set it at runtime with this code:
View someView = findViewById(R.id.some_view);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
someView.setSystemUiVisibility(someView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
And it actually works fine. But question is how to properly set a status bar mode to dark at runtime?
I already tried these variants:
// Makes status bar mode dark, but also hides it along with all navigation views.
someView.setSystemUiVisibility(someView.getSystemUiVisibility() | ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
// Does nothing
someView.setSystemUiVisibility(someView.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
// Also does nothing
someView.setSystemUiVisibility(someView.getSystemUiVisibility() ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
So how it can be done the right way?
Android Solutions
Solution 1 - Android
The solution posted by @Aracem is valid but, doesn't work if you try change also the background color of the status bar. In my case I do it in the following way.
To enable windowLightStatusBar(programatically,inside a Utils class for example):
public static void setLightStatusBar(View view,Activity activity){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int flags = view.getSystemUiVisibility();
flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
view.setSystemUiVisibility(flags);
activity.getWindow().setStatusBarColor(Color.WHITE);
}
}
To restore to StatusBar to the previous state:
public static void clearLightStatusBar(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Window window = activity.getWindow();
window.setStatusBarColor(ContextCompat
.getColor(activity,R.color.colorPrimaryDark));
}
}
Restoring the color of the status bar is enough, it restores also the icons colors. VERY IMPORTANT: The restore operation will not occur until the view used in setLightStatusBar(View view..) dissapears(that is, view.getVisibility()==GONE|INVISIBLE) from the screen.
Solution 2 - Android
According to Nick Butcher's project "Plaid"
public static void clearLightStatusBar(@NonNull View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int flags = view.getSystemUiVisibility();
flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
view.setSystemUiVisibility(flags);
}
}
You can find this file here.
Solution 3 - Android
I base on @Aracem and @Carlos Hernández Gil but I think it will easy to understand if we use bitwise XOR (^ operator in Java)
private void setLightStatusBar(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int flags = activity.getWindow().getDecorView().getSystemUiVisibility(); // get current flag
flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // add LIGHT_STATUS_BAR to flag
activity.getWindow().getDecorView().setSystemUiVisibility(flags);
activity.getWindow().setStatusBarColor(Color.GRAY); // optional
}
}
private void clearLightStatusBar(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int flags = activity.getWindow().getDecorView().getSystemUiVisibility(); // get current flag
flags = flags ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // use XOR here for remove LIGHT_STATUS_BAR from flags
activity.getWindow().getDecorView().setSystemUiVisibility(flags);
activity.getWindow().setStatusBarColor(Color.GREEN); // optional
}
}
Explain
First, look at SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
and setSystemUiVisibility
/**
* Flag for {@link #setSystemUiVisibility(int)}: Requests the status bar to draw in a mode that
* is compatible with light status bar backgrounds.
*/
public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
public void setSystemUiVisibility(int visibility) {
if (visibility != mSystemUiVisibility) {
mSystemUiVisibility = visibility;
...
}
}
I think 2 lines code below is quite hard to understand
flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // for set light status bar
flags = flags ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // for clear light status bar
At first look, I just think we can use simple like
flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // for set light status bar
flags = 0; // for clear light status bar (0 <=> LIGHT_STATUS_BAR <=> default systemUiVisibility)
But we should use |
and ^
because
Example, we want to set both status bar and navigationbar to light, then we will use
flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | View.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
activity.getWindow().getDecorView().setSystemUiVisibility(flags);
When we don't want status bar is light anymore, we can use
flags = View.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
activity.getWindow().getDecorView().setSystemUiVisibility(flags);
OR
flags = activity.getWindow().getDecorView().getSystemUiVisibility();
flags = flags ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
activity.getWindow().getDecorView().setSystemUiVisibility(flags);
To know more why we use |
and ^
, I think the tutorial below may help
https://medium.com/@JakobUlbrich/flag-attributes-in-android-how-to-use-them-ac4ec8aee7d1
Here is my understand. Hope this help
Solution 4 - Android
The way I switched light and dark for APIs 23-30 was a little different than these. This is a kotlin version
Since I was using Compose with the Crossfade animation to change themes, in some cases would call this function twice, hence making xor
undo itself. An alternative is an inverse or
operation. My light theme switcher-thing ended up looking like this
@Suppress("DEPRECATION")
fun invertInsets(darkTheme: Boolean, window: Window) {
if (Build.VERSION.SDK_INT >= 30) {
//Correct way of doing things
val statusBar = APPEARANCE_LIGHT_STATUS_BARS
val navBar = APPEARANCE_LIGHT_NAVIGATION_BARS
if (!darkTheme) {
window.insetsController?.setSystemBarsAppearance(statusBar, statusBar)
window.insetsController?.setSystemBarsAppearance(navBar, navBar)
} else {
window.insetsController?.setSystemBarsAppearance(0, statusBar)
window.insetsController?.setSystemBarsAppearance(0, navBar)
}
} else {
// Does bitwise operations (or to add, inverse or to remove)
// This is depreciated but the new version is API 30+ so I should have this here
val flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR or
if (Build.VERSION.SDK_INT >= 26) View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR else 0
if (!darkTheme) {
window.decorView.systemUiVisibility =
window.decorView.systemUiVisibility or flags
} else {
window.decorView.systemUiVisibility =
(window.decorView.systemUiVisibility.inv() or flags).inv()
}
}
}
The bit for API 30+ is what's not depreciated, but realistically not many phones are at API 30 so there's also the bit for lower APIs
It just calculates flags (since setting LIGHT_NAVIGATION_BARS
is API 26+) beforehand for conciseness and then either definitively sets or resets those exact flags. No and
or xor
funny buisiness. or
will always set the flags to 1
, and the inverse or thing will always set the flags to 0
. This is only possible because both SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
and SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
are one bit, however. Otherwise it would probably need to use xor
.
Solution 5 - Android
I put together this simple utility object that allows you to change status bar color and light status bar on/off for within any fragment. However, this relies on using the Android Jetpack Navigation component for navigation (Kotlin):
object StatusBarUtil {
fun changeStatusBarColor(activity: Activity, @ColorInt color: Int, lightStatusBar: Boolean) {
activity.window?.let { win ->
val nav = Navigation.findNavController(activity, R.id.your_nav_host_fragmen /* TODO: Use the ID of your nav host fragment */)
val currentDest = nav.currentDestination?.id
val oldColor = win.statusBarColor
val oldFlags = win.decorView.systemUiVisibility
win.statusBarColor = color
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
var flags = oldFlags
flags = if (lightStatusBar) {
flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
win.decorView.systemUiVisibility = flags
}
nav.addOnNavigatedListener { _, dest ->
if (dest.id != currentDest) {
win.statusBarColor = oldColor
win.decorView.systemUiVisibility = oldFlags
}
}
}
}
}
To use this, call the following from within any fragment's onViewCreated
:
StatusBarUtil.changeStatusBarColor(requireActivity(), someDarkColor, false)
Solution 6 - Android
There is a slight change in API 30 of the SDK and now the light status bar appearance is controlled by WindowInsetsController, which can be obtained from a Window. Below is a sample method (within an Activity) in Kotlin, combining the new API with the previously used View.setSystemUiVisibility for older Android SDK versions. Bear in mind that this only changes the system icons appearance of the status bar and the actual color of the status bar can still be set by Window.setStatusBarColor.
@Suppress("DEPRECATION")
private fun setSystemUiLightStatusBar(isLightStatusBar: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val systemUiAppearance = if (isLightStatusBar) {
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
} else {
0
}
window.insetsController?.setSystemBarsAppearance(systemUiAppearance,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS)
} else {
val systemUiVisibilityFlags = if (isLightStatusBar) {
window.decorView.systemUiVisibility or SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
window.decorView.systemUiVisibility and SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
window.decorView.systemUiVisibility = systemUiVisibilityFlags
}
}
}
Solution 7 - Android
Based on @phan-van-linh answer, I wrote this class for Xamarin Android
public static class ActivityExtensions
{
public static void SetLightStatusBar(this Activity activity)
{
int flags = (int)activity.Window.DecorView.SystemUiVisibility; // get current flag
flags |= (int)SystemUiFlags.LightStatusBar; // add LIGHT_STATUS_BAR to flag
activity.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)flags;
//activity.Window.SetStatusBarColor(Color.GRAY); // optional
}
public static void ClearLightStatusBar(this Activity activity)
{
int flags = (int)activity.Window.DecorView.SystemUiVisibility; // get current flag
flags = flags ^ (int)SystemUiFlags.LightStatusBar; // use XOR here for remove LIGHT_STATUS_BAR from flags
activity.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)flags;
//activity.Window.setStatusBarColor(Color.GREEN); // optional
}
}
Solution 8 - Android
i will make some changes in above answers.
make a class
public class DarkStatusBar {
public static void setLightStatusBar(View view, Activity activity){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int flags = view.getSystemUiVisibility();
flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
view.setSystemUiVisibility(flags);
activity.getWindow().setStatusBarColor(Color.WHITE);
}
}
}
and Call it wherever you want like this
Window window = getWindow();
View view = window.getDecorView();
DarkStatusBar.setLightStatusBar(view,this);
Solution 9 - Android
To change to light status bar use:-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
activity?.window?.decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
To change back to dark status bar :-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
activity?.window?.decorView?.systemUiVisibility = 0
Solution 10 - Android
systemUiVisibility
- is deprecated now. You can use WindowInsetsControllerCompat
instead.
private val insetsController: WindowInsetsControllerCompat? by lazy {
activity?.window?.let { window -> WindowInsetsControllerCompat(window, window.decorView) }
}
private fun setLightStatusBar(light: Boolean) {
insetsController?.isAppearanceLightStatusBars = light
}
Solution 11 - Android
Set blue background status bar with light text color kotlin
version
fun setBlueStatusBarColor(window: Window, context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
window.statusBarColor = context.getColor(R.color.colorBlue)
}else {
window.statusBarColor = context.resources.getColor(R.color.colorBlue)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
var flags: Int = window.decorView.systemUiVisibility
flags = flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
window.decorView.systemUiVisibility = flags
}
}
}
Solution 12 - Android
/**
* Changes color of the status bar icons
* @param isLight if true - shows dark icons, light else
*/
fun setStatusBarUiTheme(activity: Activity?, isLight: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity?.window?.decorView?.let {
it.systemUiVisibility = if (isLight)
it.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR // dark icons
else
it.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() // light icons
}
}
}
Solution 13 - Android
In res/styles.xml
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowLightStatusBar">true</item>
.......
</style>
<style name="AppTheme.DarkStatus" parent="AppTheme" tools:targetApi="23" >
<item name="android:windowLightStatusBar">false</item>
<item name="android:statusBarColor" >@color/status_bar_color</item>
</style>
In code
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.AppTheme_DarkStatus); //To set DarkStatusBar theme
setContentView(R.layout.activity_drawer);
....
}
Solution 14 - Android
It works for me
fun Activity.clearLightStatusBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val window = window
window.statusBarColor = ContextCompat
.getColor(this, R.color.ultramarine_blue)
}
}