This problem actually arose in a more complex situation involving orientation changes and varying layouts for portrait and landscape but in it's minimal version the problem is this:
We would like to switch back and forth between a "normal" and "fullscreen" layout, i. e.:
To switch from normal to fullscreen we are using:
public static final int EXPAND_FLAGS = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
getWindow().getDecorView().setSystemUiVisibility(EXPAND_FLAGS);
And to switch back we tried:
public static final int SHRINK_FLAGS = View.SYSTEM_UI_FLAG_VISIBLE;
getWindow().getDecorView().setSystemUiVisibility(SHRINK_FLAGS);
So, from this
we first "expand" to this:
but "shrinking" back does not work:
So, it seems that while the Activity is only allowed to draw inside of the system UI it still "thinks" it should leave space for the system UI.
So my question is: What should SHRINK_FLAGS
be in my code above or what should I do completely differently?
We are using an AppCompatActivity
with a Theme.AppCompat.Light.DarkActionBar
theme.
Seems the root of your layout has android:fitsSystemWindows="true"
. For some reason, which I'm not aware of, fitsSystemWindows
doesn't work well when changing system UI flags. Fortunately, there's a workaround for this problem.
First, set expand system UI flags unconditionally:
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
);
Then remove android:fitsSystemWindows="true"
from all ancestor views of the layout.
After that create a layout that will handle system insets changes and add or remove padding based on the value of its property. That's the most complicated part. You have to handle system insets differently depending on platform version. For versions below 20 (Kitkat Watch) you have to override View.fitSystemWindows()
method:
@Override
protected boolean fitSystemWindows(final Rect insets) {
if (mFit) {
setPadding(insets.left, insets.top, insets.right, insets.bottom);
// Do not propagate the system insets further.
return true;
} else {
setPadding(0, 0, 0, 0);
// Do not consume the insets and allow other views handle them.
return false;
}
}
For platform version greater or equal than 20 you need to override View.onApplyWindowInsets()
method and to the same processing as in the previous method:
@Override
public WindowInsets onApplyWindowInsets(final WindowInsets insets) {
if (mFit) {
setPadding(
insets.getSystemWindowInsetLeft(),
insets.getSystemWindowInsetTop(),
insets.getSystemWindowInsetRight(),
insets.getSystemWindowInsetBottom()
);
return insets.consumeSystemWindowInsets();
} else {
setPadding(0, 0, 0, 0);
return insets;
}
}
And finally create a setter for the mFit
field, which will notify the system that it must apply insets again:
mFit = fit;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
requestApplyInsets();
} else {
requestFitSystemWindows();
}
Now you can call setFit(false)
to make your views layout behind the system UI and setFit(true)
to make the layout fit system windows.
You can get the source code for this layout here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With