Reputation: 2491
I'm trying to draw content under both status bar and navigation bar with them being completely transparent while also taking advantage of fitsSystemWindows="true"
.
For the status bar, fitsSystemWindows="true"
along with the following code works fine.
getWindow() .getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
getWindow().setStatusBarColor(Color.TRANSPARENT);
However, when I add the following line to draw content under the navigation bar, the insets that we get from fitsSystemWindows="true"
are no longer available.
getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
Some solutions include obtaining the statusbar and navigation bar heights like so
getResources().getDimensionPixelSize(getResources().getIdentifier("status_bar_height", "dimen", "android"));
and using it as padding.
However, this method is discouraged.
Is there any way to maintain the behaviour of fitsSystemWindows="true" while drawing content under both the status bar and the navigation bar? Thanks in advance.
Upvotes: 0
Views: 1553
Reputation: 376
Use the flag
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window w = getWindow();
w.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
w.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
w.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
Then push your view using these two methods
@SuppressLint("NewApi")
public static boolean hasImmersive(Context ctx) {
if (!cached) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
hasImmersive = false;
cached = true;
return false;
}
Display d = ((WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
DisplayMetrics realDisplayMetrics = new DisplayMetrics();
d.getRealMetrics(realDisplayMetrics);
int realHeight = realDisplayMetrics.heightPixels;
int realWidth = realDisplayMetrics.widthPixels;
DisplayMetrics displayMetrics = new DisplayMetrics();
d.getMetrics(displayMetrics);
int displayHeight = displayMetrics.heightPixels;
int displayWidth = displayMetrics.widthPixels;
hasImmersive = (realWidth > displayWidth) || (realHeight > displayHeight);
cached = true;
}
return hasImmersive;
}
Apply padding accordingly
if (hasImmersive(this)) {
yourView.setPadding(0, 0, 0, getSoftButtonsBarHeight());
}
And get the height of System soft button
@SuppressLint("NewApi")
private int getSoftButtonsBarHeight() {
// getRealMetrics is only available with API 17 and +
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int usableHeight = metrics.heightPixels;
getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
int realHeight = metrics.heightPixels;
if (realHeight > usableHeight)
return realHeight - usableHeight;
else
return 0;
}
return 0;
}
EDIT: To preserve the fitsSystemWindows="true"
behaviour for the notification bar, add the FLAG_LAYOUT_NO_LIMITS
and other flags after the window insets have been applied to your View
which has fitsSystemWindows
set to true
. This will ensure that the insets have been applied normally before applying the no-limit flag.
yourViewWithFitsSystemWindows.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
if(insets.getSystemWindowInsetTop() == 0) return insets; // This is needed.
v.onApplyWindowInsets(insets);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
// add other flags
return insets;
}
});
This, along with setting the bottom padding, gives the desired result.
Upvotes: 2