Arthurius
Arthurius

Reputation: 87

Android - Problem with fitsSystemWindows property

I need your help to understand a strange behavior. When I set the fitsSystemWindows property to 'true', the navigation bar hides some part of my layout, see the image below :

Result with android:fitsSystemWindows="true"

When I set to false, I have this behavior (it's OK) :

Result with android:fitsSystemWindows="false"

When I read the Android documentation and many posts on Stackoverflow, I understand it should be the exact opposite of this behaviour : https://developer.android.com/reference/android/view/View#attr_android:fitsSystemWindows. The first case with fitsSystemWindows='true' should be OK and the second case should be hidden by the navigation bar, am I wrong ?

Could someone explain me what's happened ? My targetVersionSdk is 29 and I tested it on many versions (Android 6,7 10 and 11). Maybe it's specific to CoordinatorLayout ? Thanks for your explanations :)

Here is my xml layout :

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="[true or false]">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            android:minHeight="?attr/actionBarSize"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
    </com.google.android.material.appbar.AppBarLayout>

    [...]

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_marginBottom="@dimen/activity_vertical_margin"
        android:layout_marginEnd="@dimen/activity_vertical_margin"
        android:src="@drawable/ic_arrow_forward_white_24dp" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

Upvotes: 2

Views: 3428

Answers (1)

Susan Thapa
Susan Thapa

Reputation: 581

I think the view is working as expected. First you need to understand the insets and how it's passed around. The default behavior of fitsSystemWindow is to consume all the insets and apply them as padding. But ViewGroups like CoordinatorLayout, DrawerLayout override this behavior.

Here is the snippet of the code in CoordinatorLayout that overrides the behavior.

    private void setupForInsets() {
        if (Build.VERSION.SDK_INT < 21) {
            return;
        }

        if (ViewCompat.getFitsSystemWindows(this)) {
            if (mApplyWindowInsetsListener == null) {
                mApplyWindowInsetsListener =
                        new androidx.core.view.OnApplyWindowInsetsListener() {
                            @Override
                            public WindowInsetsCompat onApplyWindowInsets(View v,
                                    WindowInsetsCompat insets) {
                                return setWindowInsets(insets);
                            }
                        };
            }

            ViewCompat.setOnApplyWindowInsetsListener(this, mApplyWindowInsetsListener);

            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
        } else {
            ViewCompat.setOnApplyWindowInsetsListener(this, null);
        }
    }

As you can see applying fitsSystemWindow in CoordinatorLayout causes it to render the contents under the system UI. What you need to do is to add the insets provided by the system and apply it as margin or padding to the top and bottom views.

You can use setOnApplyWindowInsetsListener() to listen for insets and apply it. Let's say you havebottomNav as bottom view then you can do something like this to account for the bottom inset.

ViewCompat.setOnApplyWindowInsetsListener(bottomNav) { view, insets ->
    bottomNav.updatePadding(bottom = insets.systemWindowInsetBottom)
    insets
}

You can learn more about insets in this blog post.

Upvotes: 3

Related Questions