David Heisnam
David Heisnam

Reputation: 2491

Draw content under both status bar and navbar while maintaining fitsSystemWindows="true" behaviour

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);

enter image description here

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);

enter image description here

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

Answers (1)

Sai Jayant
Sai Jayant

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;
    }

Screenshots Here is the screen shot

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

Related Questions