fitsSystemWindows="true" doesn't work after calling setContentView()

2.1k Views Asked by At

For an app I'm developing I wanted to reload the UI after a user input (basically resetting it completely after they made changes to it). I wanted to try avoiding destroying/recreating the activity and use setContentView() instead because it's a lot faster.

However when I do that I'm having an issue: the newly created UI doesn't respect the fitsSystemWindows="true" and part of it ends up behind the android's status bar.

I managed to boil it down to that code example to test it :

layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mainContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <Button
        android:text="Button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button" />
 </LinearLayout>

MainActivity.java

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout);

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                reloadUI();
            }
        });

    }

    public void reloadUI() {
        setContentView(R.layout.layout);
    }
}

When I load the app, I get the expected layout, that is a simple button on top of the screen, right below the status bar:

enter image description here

However, once I click the button which calls setContentView a second time (showing the same XML), the Button gets behind the status bar:

enter image description here

Calling mainContainer.getMeasuredHeight() to check what happens gives me 1848px on the first start of the app (on a screen that is 1920px tall, so its height is 72px less than the whole screen, 72px being the height of the status bar), but once I call setContentView again the mainContainer.getMeasuredHeight() gives me 1920px.

Am I missing something here? I could force the mainContainer to stick to a 1848px height with a 72px top padding, but I'd prefer to avoid an ugly hack like this.

2

There are 2 best solutions below

0
On

I had the same problem. My solution is to change the rootView marginTop or paddingTop to adapt to the View manually.

1
On

So, what you want is to ask the framework to dispatch once more WindowInsets to your root view. That's precisely what ViewCompat.requestApplyInsets(View) will do:

Ask that a new dispatch of View.onApplyWindowInsets(WindowInsets) be performed. This falls back to View.requestFitSystemWindows() where available.

Applying just one line should resolve all your concerns:



    public void reloadUI() {
        setContentView(R.layout.layout);
        // `R.id.mainContainer` is the id of the root view in `R.layout.layout`
        ViewCompat.requestApplyInsets(findViewById(R.id.mainContainer));
    }