Search code examples
androidandroid-layoutandroid-fragmentsnavigation-drawerwindowinsets

fitsSystemWindows on Fragment, Activity and DrawerLayout combination


Problem

The activity layout has a DrawerLayout with a FrameLayout as content to Fragment and a LinearLayout to be opened and closed by drawer. The fragment used on content has a FrameLayout with fitsSystemWindows flag and a Toolbar.

If I insert the fragment at onCreate() all occurs as expected, the FrameLayout starts on position 0 and the Toolbar starts below the statusbar. But outside the onCreate() the same code fails, the fitsSystemWindows not works with FrameLayout.

An important point, I need the Toolbar and fitsSystemWindows flag on Fragment and not on Activity.

How to get the same behavior seen at onCreate() moment?

Code

Main Activity

public class MainActivity extends AppCompatActivity {

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

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = getWindow();
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
            window.setStatusBarColor(Color.TRANSPARENT);
        }

        setContentView(R.layout.activity);

        findViewById(R.id.item).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Out of onCreate fails
                replaceFragment();
            }
        });

        // With onCreate all OK
        replaceFragment();
    }

    private void replaceFragment() {
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.content, new MainFragment())
                .commit();
    }

}

Main Layout

<android.support.v4.widget.DrawerLayout
    android:id="@+id/drawer"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <LinearLayout
        android:layout_width="256dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="#ff0000"
        android:orientation="vertical"
        tools:layout_gravity="">

        <View
            android:layout_width="match_parent"
            android:layout_height="168dp"
            android:layout_marginBottom="16dp"
            android:background="#00ffff"/>

        <TextView
            android:id="@+id/item"
            android:layout_width="match_parent"
            android:layout_height="80dp"
            android:background="#ff00ff"
            android:gravity="center"
            android:text="Click me"/>

    </LinearLayout>

</android.support.v4.widget.DrawerLayout>

Fragment

public class MainFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 
                                                @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment, container, false);
    }

}

Fragment layout:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#00ff00"
        android:fitsSystemWindows="true">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="56dp"
            android:background="#0000ff"/>

    </FrameLayout>

</RelativeLayout>

Example

The LinearLayout to be opened is in red. The FrameLayout with the fitsSystemWindows flag is in green and the toolbar is in blue, the cyan view is working all the time, it need start at y position 0. The expected is Green and blue like the first time, but in the second time the blue is the unique viewed.

enter image description here


Solution

  • So, the problem is, that the when the content is being changed, system will no longer dispatch those insets to the view hierarchy.

    This can be easily solved with ViewCompat.requestApplyInsets(View) API. That would force the system to dispatch WindowInsets once again.

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

    So, in your example performing this changes would result in expected behavior:

    
        private void replaceFragment() {
          getSupportFragmentManager().beginTransaction()
              .replace(R.id.content, new MainFragment())
              // NOTE, we are performing `commitNow()` instead of ordinary `commit()`,
              // Because we want this commit to happen sychronously/immediately
              .commitNow();
    
          // Ask the framework to dispatch window insets once more to the root of your view hierarchy
          ViewCompat.requestApplyInsets(findViewById(R.id.drawer));
        }
    
    

    enter image description here