I've exhausted essentially every post I've found regarding changing the status bar color programmatically, but none of them touch on this specific case.
I have an Activity that contains a ViewPager and a BottomNavigationView. The ViewPager holds three Fragments and the BottomNavigationView moves between them with smoothscroll turned off.
As the first of the three Fragments is a map, I'd like the status bar to be transparent when the first Fragment is shown, but revert back to its opaque color on the other two Fragments.
Here's some code that shows how I'm attempting to set and revert the status bar states.
private class TabSelectedObserver implements Observer<Integer> {
#Override
public void onChanged(#Nullable Integer selectedTab) {
if (selectedTab != null) {
activityMainBinding.mainPager.setCurrentItem(selectedTab, false);
switch (selectedTab) {
case 0:
applyTransparentStatusBarTheme();
break;
default:
applyOpaqueStatusBarTheme();
break;
}
}
}
}
private void applyTransparentStatusBarTheme() {
hideSystemUi();
getWindow().setStatusBarColor(ContextCompat.getColor(this, android.R.color.transparent));
}
private void applyOpaqueStatusBarTheme() {
showSystemUi();
getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.rippallete_700));
}
private void hideSystemUi() {
// Set flags for hiding status bar and navigation bar
mSystemUiVisibility = mSystemUiVisibility
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
getWindow().getDecorView().setSystemUiVisibility(mSystemUiVisibility);
}
private void showSystemUi() {
// Reset flags for hiding status bar and navigation bar
mSystemUiVisibility = mSystemUiVisibility
& ~View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
getWindow().getDecorView().setSystemUiVisibility(mSystemUiVisibility);
}
Here's a gif showing the behavior I end up with
Every thing I tried with fitSystemWindows didn't produce the result I wanted.
If anyone has any suggestions for how you would give each Fragment an individual status bar color, with 1 being transparent, I'd really appreciate it.
So assuming you're setting the status bar colours based on the position of whatever tab you have selected you could do something similar to this:
private void updateStatusBarColour(int tabPosition) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
switch (tabPosition) {
case FIRST_TAB:
case SECOND_TAB:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mRootView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
window.setStatusBarColor(ContextCompat.getColor(this, R.color.colorPrimaryLight));
break;
case THIRD_TAB:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mRootView.setSystemUiVisibility(0);
}
window.setStatusBarColor(ContextCompat.getColor(this, R.color.colorPrimary));
break;
case FOURTH_TAB:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mRootView.setSystemUiVisibility(0);
}
window.setStatusBarColor(ContextCompat.getColor(this, R.color.colorDarkTan));
break;
}
}
}
Note that I call setSystemUiVisibility on mRootView, where mRootView is the root parent view containing the ViewPager (or whatever is the root layout of your containing Activity). The call specifically sets the theme of the status bar so that status bar items (like the battery symbol) can be visible on dark or light background depending on what colour you end up setting.
Related
I tried to color my bottom navigation bar
In some android versions it's look white background with white icons
protected void updateNavigationBarColor(Window window, boolean isLight) {
if (window == null) {
return;
}
View decorView = window.getDecorView();
if (decorView == null) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
window.setNavigationBarColor(Utils.getColor(R.color.navigation_bar_color));//black or white
WindowInsetsControllerCompat controller = new WindowInsetsControllerCompat(window, decorView);
controller.setAppearanceLightNavigationBars(isLight);
} else {
// Do nothing
}
}
The navigation bar background color is exactly what I ask for. but the 3 buttons (icons) are stay in always light
In themes.xml added to my theme
<item name="android:navigationBarColor">#color/transparent</item>
<item name="android:windowLightNavigationBar">true</item>
I have an app which is based on single activity and multiple fragments and some fragments needs to show into fullscreen when enters fragment and exit from fullscreen when exit. I am currently using flags to show fullscreen in Android Kitkat but its not optimal way i think. I also read ImmersiveMode but it's not working in lower Android version. Currently i am using these methods for fullscreen enter and exit.
//This method not showing transparent status bar also navigation bar and not showing system icons either
public static void setFullscreen(Activity activity) {
if (Build.VERSION.SDK_INT > 10) {
int flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_FULLSCREEN;
boolean isImmersiveAvailable = android.os.Build.VERSION.SDK_INT >= 19;
if (isImmersiveAvailable) {
flags |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
activity.getWindow().getDecorView().setSystemUiVisibility(flags);
} else {
activity.getWindow()
.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
public static void exitFullscreen(Activity activity) {
if (Build.VERSION.SDK_INT > 10) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
} else {
activity.getWindow()
.setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
}
}
public static void ShowHideFullscreen(boolean isFullscreen,Context cntx){
int newUiOptions = 0;
if (isFullscreen){
// BEGIN_INCLUDE (get_current_ui_flags)
// The UI options currently enabled are represented by a bitfield.
// getSystemUiVisibility() gives us that bitfield.
int uiOptions = ((Activity)cntx).getWindow().getDecorView().getSystemUiVisibility();
newUiOptions = uiOptions;
// END_INCLUDE (get_current_ui_flags)
// BEGIN_INCLUDE (toggle_ui_flags)
boolean isImmersiveModeEnabled =
((uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) == uiOptions);
if (isImmersiveModeEnabled) {
Log.d(TAG, "Turning immersive mode mode off. ");
} else {
Log.d(TAG, "Turning immersive mode mode on.");
}
// Navigation bar hiding: Backwards compatible to ICS.
if (Build.VERSION.SDK_INT >= 14) {
newUiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}
// Status bar hiding: Backwards compatible to Jellybean
if (Build.VERSION.SDK_INT >= 16) {
newUiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN;
}
// Immersive mode: Backward compatible to KitKat.
// Note that this flag doesn't do anything by itself, it only augments the behavior
// of HIDE_NAVIGATION and FLAG_FULLSCREEN. For the purposes of this sample
// all three flags are being toggled together.
// Note that there are two immersive mode UI flags, one of which is referred to as "sticky".
// Sticky immersive mode differs in that it makes the navigation and status bars
// semi-transparent, and the UI flag does not get cleared when the user interacts with
// the screen.
if (Build.VERSION.SDK_INT >= 18) {
newUiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
((Activity)cntx).getWindow().getDecorView().setSystemUiVisibility(newUiOptions);
}else {
((Activity)cntx).getWindow().clearFlags(newUiOptions);
}
}
These are method we use to enter or exit from fullscreen in fragment with hiding action bar. We use these to entered in fullscreen on onCreate and exit on onPause but sometimes it fails to show desired result.
We need to know what are the optimal way to show fullscreen fragment on lower API too(KITKAT) and also which are the best way to hide toolbar when showing fullscreen because our code ((AppCompatActivity)Objects.requireNonNull(getActivity())).getSupportActionBar().hide();
throws nullpointerexception sometimes.
Our issue:
Show fullscreen fragment with transparent status bar and drawing system icon over fullscreen fragment and transparent navigation bar.
Hiding and showing toolbar with method which safe to call without getting NullpointerExepection
We can target android Kitkat as lowest android version.
Fragment Fullscreen example with system icons and transparent navigation bar.
Update: I set IMMERSIVE mode on my fragment onResume and reset it back at onStop by these method.
private void hideSystemUI() {
// Enables regular immersive mode.
// For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE.
// Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
View decorView = getActivity().getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
// Set the content to appear under the system bars so that the
// content doesn't resize when the system bars hide and show.
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
// Hide the nav bar and status bar
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN);
((AppCompatActivity) Objects.requireNonNull(getActivity())).getSupportActionBar().hide();
}
// Shows the system bars by removing all the flags
// except for the ones that make the content appear under the system bars.
private void showSystemUI() {
View decorView = getActivity().getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
((AppCompatActivity) Objects.requireNonNull(getActivity())).getSupportActionBar().show();
}
It shows my fragment in fullscreen with status bar hidden (that's not what i want) and my fragments belong to Navigation Drawer. when i press back button from fullscreen fragment it shows my status bar over toolbar in previous fragment.
I solved this issue by creating a method which clears desired flags at onStop and add flags at onResume
#Override
public void onResume() {
super.onResume();
App_Functions.transparentStatusBar(getActivity(),true,false);
}
#Override
public void onStop() {
super.onStop();
App_Functions.transparentStatusBar(getActivity(),false,false);
}
if i want to show transparent statusbar but navigation bar should be same then i use true in first parameters of method and for clearing that flag just use false. Same way i am using true for complete fullscreen and false for clearing fullscreen flags
public static void transparentStatusBar(Activity activity, boolean isTransparent, boolean fullscreen) {
if (isTransparent){
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
((AppCompatActivity) Objects.requireNonNull(activity)).getSupportActionBar().hide();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
defaultStatusBarColor = activity.getWindow().getStatusBarColor();
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// FOR TRANSPARENT NAVIGATION BAR
//activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
Log.d(TAG,"Setting Color Transparent "+Color.TRANSPARENT+ " Default Color "+defaultStatusBarColor);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.d(TAG,"Setting Color Trans "+Color.TRANSPARENT);
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
}else {
if (fullscreen){
View decorView = activity.getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
}else {
((AppCompatActivity) Objects.requireNonNull(activity)).getSupportActionBar().show();
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
activity.getWindow().setStatusBarColor(defaultStatusBarColor);
}else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
}
}
}
Please feel free to recommend better way to use this method across all platform.
How to handle the toolbar from fragment?
Where to put the toolbar is highly opinionated. But I recommend you to put the toolbar in each fragment xml file rather than keeping one toolbar for the whole application in the Activity xml file. See this sample app by Google, they are also placing the AppbarLayout and toolbar per Fragment rather than keeping it in the activity layout file.
So if you place the toolbar with in the fragment, you can easily hide/show it from the fragment without the fear of getting null pointer exceptions.
So your activity xml file will simply contain the NavHost Fragment only. So the whole screen will be managed by the currently visible Fragment :)
How to get the transparent statusbar for older API levels?
Devices below kitkat would not support transparent statusbar feature. Kitkat supports transluent statusbar, not transparent. So you can use transparent statusbar for API level lollipop or above. If the device version is below lollipop, ignore it as the platform does not support the feature. So rest of my answer assumes you want to achieve transparent status bar when the device version is lollipop or above.
Well then, How to get transparent status-bar for lollipop and above for selected fragments?
We need to create a custom NavHostFragment. This is required because, during fragment transitions, multiple fragment's view hierarchies can be added at the same time. If one consumes window insets, the other might not be laid out properly. To workaround that, we need to make sure we dispatch the insets to all children, regardless of how they are consumed.
class DispatchInsetsNavHostFragment : NavHostFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
view.setOnApplyWindowInsetsListener { v, insets ->
(v as? ViewGroup)?.forEach { child ->
child.dispatchApplyWindowInsets(insets)
}
insets
}
}
}
}
Wrap your nav host fragment with a Framelayout as shown below and put android:fitsSystemWindows="true"
<FrameLayout
android:id="#+id/navHostContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<fragment
android:id="#+id/navHostFragment"
android:name="com.yourdomain.app.DispatchInsetsNavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/main" />
</FrameLayout>
Set the following flags to the navHostContainer which we defined above, from your MainActivity class onCreate method after setContentView.
val navHostContainer: FrameLayout = findViewById(R.id.navHostContainer)
navHostContainer.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
// Make the content ViewGroup ignore insets so that it does not use the default padding
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
navHostContainer.setOnApplyWindowInsetsListener(NoopWindowInsetsListener)
}
Here is the NoopWindowInsetsListener
object NoopWindowInsetsListener : View.OnApplyWindowInsetsListener {
override fun onApplyWindowInsets(v: View, insets: WindowInsets): WindowInsets {
return insets
}
}
Thats all, from now on, your fragments has the complete ownership to the statusbar area. Your views will be drawn behind the transparent status bar. For some fragments, you may not want the statusbar to overlap your content, for those fragments, simply put android:fitsSystemWindows="true" to your root layout in the Fragment xml file.
How to set custom statusbar color for some Fragments?
Use the app:statusBarBackground attribute of CoordinatorLayout with android:fitsSystemWindows="true"
That's all. Happy coding :)
1- in style xml file change AppTheme parent to NoActionBar:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
2- use this method in your activity for transparent statusBar
private void hideStatusBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
getWindow().setStatusBarColor(ContextCompat.getColor(this, android.R.color.transparent));
}
}
3- match_parent frameLayout tag in your activity_main(xml) for full screen the fragment
I've been searching everywhere but I'm at a loss about that : trying to activate immersive mode on a project;
Nearly everything works fine, except the background of my status bar always stays there, spoiling the immersion...
I have included a screenshot of the screen before and after activating the immersive mode, and set the "colorPrimaryDark" to full green for max contrast :
screenshots showing the background of the status bar when nothing should be there
The code I used and reinserted in a blank project to isolate this problem comes straight from the google dev examples, in my MainActivity, I have :
private final String TAG = "DEBUG::" + this.getClass().getSimpleName();
private final int INITIAL_HIDE_DELAY = 1500;
private View decorView;
private Toolbar toolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//setting needed decorView for fullscreen behavior
decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(onSystemUiVisibilityChangeListener);
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Log.i(TAG, "onWindowFocusChanged::hasFocus = " + hasFocus);
if (hasFocus) {// When the window gains focus, hide the system UI.
delayedHide(INITIAL_HIDE_DELAY);
} else {// When the window loses focus, cancel any pending hide action.
mHideHandler.removeMessages(0);
}
}
private void hideSystemUI() {
Log.i(TAG, "hideSystemUI");
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
uiOptions |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
uiOptions |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
uiOptions |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
uiOptions |= View.SYSTEM_UI_FLAG_FULLSCREEN;
uiOptions |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
uiOptions |= View.SYSTEM_UI_FLAG_IMMERSIVE;
decorView.setSystemUiVisibility(uiOptions);
}
private final Handler mHideHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
hideSystemUI();
}
};
private void delayedHide(int delayMillis) {
Log.i(TAG, "delayedHide");
mHideHandler.removeMessages(0);
mHideHandler.sendEmptyMessageDelayed(0, delayMillis);
}
private View.OnSystemUiVisibilityChangeListener onSystemUiVisibilityChangeListener =
new View.OnSystemUiVisibilityChangeListener() {
#Override
public void onSystemUiVisibilityChange(int visibility) {
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
// The system bars are visible
getSupportActionBar().show();
delayedHide(INITIAL_HIDE_DELAY);
} else {
// The system bars are NOT visible
getSupportActionBar().hide();
}
}
};
I wonder if my problem might come from layout or style files, but those are raw from project generation...
I hope someone out there can point me to where I failed!
Thanks in advance!
EDIT : I found that removing : android:fitsSystemWindows="true" from my activity's layout file allows a real fullscreen mode, but then, my ActionBar is partly hidden behind the StatusBar -when showing. Could it be that when I set my getSupportActionBar().show(); in onSystemUiVisibilityChangeListener, it gets drawn too soon?
EDIT 2 : How I understand this so far is that I only have 2 choices regarding the position/size of my content (action bar included) :
top of the screen, which will show the actionBar partially hidden by the statusbar,
or below the statusBar's bottom, which will leave me with a "hole" when the statusBar is hidden -_-
I am now looking for a solution to animate the ActionBar off-screen/on-screen by myself inside my onSystemUiVisibilityChangeListener method, but can't find a way to grab its View to do so, solutions posted there https://stackoverflow.com/a/21125631/6463888 seem out of date...
I met the same problem today. But I didn't solve this issue by setSystemUiVisibility. I solve it using following method:
hide:enter full screen
mActivity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
show:normal display
mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
So,
the way I solved this may seem a bit stretched, but I'm only a beginner, so feel free to comment!
I don't use getSupportActionBar().show(); or getSupportActionBar().hide(); anymore, but I managed to grab the View that contains the ActionBar which is an AppBarLayout, and I animated this View instead. So I call a small function animateActionBarInOrOut to animate it on or off screen inside onSystemUiVisibilityChange :
private void animateActionBarInOrOut(boolean appears){
Log.i(TAG, "animateActionBarInOrOut::actual position = " + toolbar.getY());
if(appears){
toolbar.animate().translationY(48).alpha(1); // move it out of the screen
}else{
toolbar.animate().translationY(-48).alpha(0); // move it out of the screen
}
}
Although this is not exactly an answer to the initial question, it works as a solution to the problem, one just has to move the content accordingly...
Hi your problem is caused by StatusBar nature, it has a separate layout from your main content and you need to set his color manually. For example when you call onSystemUiVisibilityChange() you can take the color of your background and, after set the color of StatusBar with that color. This is a workaround to avoid 2 different background colors.
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?
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.
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.
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
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.
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
}
UPD:
Above constructor for WindowInsetsControllerCompat is deprecated, so use the following instantiation instead:
private val insetsController: WindowInsetsControllerCompat? by lazy {
activity?.window?.decorView?.let(ViewCompat::getWindowInsetsController)
}
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
}
}
}
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)
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
}
}
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
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);
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)
}
}
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
}
}
}
/**
* 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
}
}
}
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);
....
}
For people who doesn't have a Window instance. It's also possible to do with a View instance (For API 30) :
fun setLightStatusBar(view: View) = view.windowInsetsController?.setSystemBarsAppearance(
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
)
fun clearLightStatusBar(view: View) = view.windowInsetsController?.setSystemBarsAppearance(
0,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
)
I need some help toggling fullscreen mode. I have a setting in a preference screen to go fullscreen. In my main activity's onResume I have:
if(mFullscreen == true) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
} else
{
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
But this doesn't seem to work because it needs to be called before setContentView right?
... But Also, I have requestWindowFeature(Window.FEATURE_NO_TITLE); before setContentView and it takes away the title AND status bar... Can anybody offer some help?
---Edit---
Ok, I had a bug that was causing this to not work. So it actually does. Now, I just need to know how to toggle the title bar.
private void setFullscreen(boolean fullscreen)
{
WindowManager.LayoutParams attrs = getWindow().getAttributes();
if (fullscreen)
{
attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
}
else
{
attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
}
getWindow().setAttributes(attrs);
}
/**
* toggles fullscreen mode
* <br/>
* REQUIRE: android:configChanges="orientation|screenSize"
* <pre>
* sample:
* private boolean fullscreen;
* ................
* Activity activity = (Activity)context;
* toggleFullscreen(activity, !fullscreen);
* fullscreen = !fullscreen;
* </pre>
*/
private void toggleFullscreen(Activity activity, boolean fullscreen) {
if (Build.VERSION.SDK_INT >= 11) {
// The UI options currently enabled are represented by a bitfield.
// getSystemUiVisibility() gives us that bitfield.
int uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
int newUiOptions = uiOptions;
boolean isImmersiveModeEnabled =
((uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) == uiOptions);
if (isImmersiveModeEnabled) {
Log.i(context.getPackageName(), "Turning immersive mode mode off. ");
} else {
Log.i(context.getPackageName(), "Turning immersive mode mode on.");
}
// Navigation bar hiding: Backwards compatible to ICS.
if (Build.VERSION.SDK_INT >= 14) {
newUiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}
// Status bar hiding: Backwards compatible to Jellybean
if (Build.VERSION.SDK_INT >= 16) {
newUiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN;
}
// Immersive mode: Backward compatible to KitKat.
// Note that this flag doesn't do anything by itself, it only augments the behavior
// of HIDE_NAVIGATION and FLAG_FULLSCREEN. For the purposes of this sample
// all three flags are being toggled together.
// Note that there are two immersive mode UI flags, one of which is referred to as "sticky".
// Sticky immersive mode differs in that it makes the navigation and status bars
// semi-transparent, and the UI flag does not get cleared when the user interacts with
// the screen.
if (Build.VERSION.SDK_INT >= 18) {
newUiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
activity.getWindow().getDecorView().setSystemUiVisibility(newUiOptions);
} else {
// for android pre 11
WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();
if (fullscreen) {
attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
} else {
attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
}
activity.getWindow().setAttributes(attrs);
}
try {
// hide actionbar
if (activity instanceof ActionBarActivity) {
if (fullscreen) ((ActionBarActivity) activity).getSupportActionBar().hide();
else ((ActionBarActivity) activity).getSupportActionBar().show();
} else if (Build.VERSION.SDK_INT >= 11) {
if (fullscreen) activity.getActionBar().hide();
else activity.getActionBar().show();
}
} catch (Exception e) {
e.printStackTrace();
}
// set landscape
// if(fullscreen) activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
// else activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
}
my code working fine with android 2.3 and 4.4.2
Since Jellybean (4.1) there is a new method that doesn't rely on the WindowManager. Instead use setSystemUiVisibility off of the Window, this gives you more granular control over the system bars than using WindowManager flags. This is how you enable fullscreen:
if (Build.VERSION.SDK_INT < 16) { //ye olde method
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else { // Jellybean and up, new hotness
View decorView = getWindow().getDecorView();
// Hide the status bar.
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
// Remember that you should never show the action bar if the
// status bar is hidden, so hide that too if necessary.
ActionBar actionBar = getActionBar();
actionBar.hide();
}
And this is how you revert the above code:
if (Build.VERSION.SDK_INT < 16) { //ye olde method
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else { // Jellybean and up, new hotness
View decorView = getWindow().getDecorView();
// Hide the status bar.
int uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
decorView.setSystemUiVisibility(uiOptions);
// Remember that you should never show the action bar if the
// status bar is hidden, so hide that too if necessary.
ActionBar actionBar = getActionBar();
actionBar.show();
}
There's a shorter toggle full screen method implementation:
private void toggleFullscreen() {
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.flags ^= WindowManager.LayoutParams.FLAG_FULLSCREEN;
getWindow().setAttributes(attrs);
}
It uses bitwise XOR logic to toggle FLAG_FULLSCREEN.
My solution combines answers from:
cprcrack's answer to this question
Holoeverywhere : how to programmatically remove at runtime the action bar from an activity
Android: Programmatically detect if device has hardware menu button
I added these methods to my Activity. To toggle full screen, use setFullScreen(!isFullScreen()).
public boolean isFullScreen() {
return (getWindow().getAttributes().flags &
WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
}
#SuppressLint("NewApi")
public void setFullScreen(boolean full) {
if (full == isFullScreen()) {
return;
}
Window window = getWindow();
if (full) {
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
if (Build.VERSION.SDK_INT >= 11) {
if (full) {
getActionBar().hide();
} else {
getActionBar().show();
}
}
}
In my case, I wanted a menu button to do the toggling. The problem: on a device without a hardware menu button, hiding the action bar also hides the toggle to return from full screen. So, I added some extra logic so it only hides the action bar if the device has a hardware menu button. Note that devices running SDK 11-13 did not have one.
if (Build.VERSION.SDK_INT >= 14
&& ViewConfiguration.get(this).hasPermanentMenuKey()))) {
if (full) {
getActionBar().hide();
} else {
getActionBar().show();
}
}
Older devices (running Gingerbread or earlier) have a Title Bar instead of an Action Bar. The following code will hide it, but be aware that the Title Bar cannot be shown/hidden once the activity has started. I included a message to the user in my help menu stating that changes to full screen may not take full effect on older devices until they restart the app/activity (which of course assumes you persist their selection and execute this code only if they want it full screen).
// call before setContentView()
if (Build.VERSION.SDK_INT < 11) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
}
Kotlin solution for the fullscreen.
if (isFullscreen) {
WindowInsetsControllerCompat(activity?.window!!, root).apply {
hide(WindowInsetsCompat.Type.systemBars())
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
} else {
WindowInsetsControllerCompat(activity?.window!!, root)
.show(WindowInsetsCompat.Type.systemBars())
}