I'm trying to setup fullscreen activity for Android 10 using insets. I wanted to have an image in toolbar drawn behind status bar. I've tried to use android:fitsSystemWindows flag in different combinations, but it doesn't work, AppBarLayout doesn't have correct padding and status bar slightly overlaps toolbar menu controls. So I've used convenient WindowInsetsCompat wrapper, Insetter library by Chris Banes, to set paddings according to system window insets.

Here is my layout:

<androidx.coordinatorlayout.widget.CoordinatorLayout 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/book_activity_root"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:animateLayoutChanges="true"
  tools:context="com.bookcrossing.mobile.ui.bookpreview.BookActivity"
  >

  <androidx.core.widget.NestedScrollView
    android:id="@+id/nestedScrollView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    >
    ...
  </androidx.core.widget.NestedScrollView>
  <com.google.android.material.appbar.AppBarLayout
    android:id="@+id/toolbarContainer"
    android:layout_width="match_parent"
    android:layout_height="220dp"
    >
    <com.google.android.material.appbar.CollapsingToolbarLayout
      android:id="@+id/collapsingToolbarContainer"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:contentScrim="?attr/colorPrimary"
      app:expandedTitleGravity="bottom"
      app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
      app:titleEnabled="true"
      app:toolbarId="@id/toolbar"
      >

      <ImageView
        android:id="@+id/cover"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:contentDescription="@string/cover_description"
        android:scaleType="centerCrop"
        android:cropToPadding="true"
        app:layout_collapseMode="parallax"
        app:layout_collapseParallaxMultiplier="0.5"
        />

      <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:layout_collapseMode="pin"
        app:layout_scrollFlags="scroll|enterAlways"
        tools:title="War and Peace"
        />

    </com.google.android.material.appbar.CollapsingToolbarLayout>
  </com.google.android.material.appbar.AppBarLayout>
  <com.google.android.material.floatingactionbutton.FloatingActionButton
    android:id="@+id/favorite"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="@dimen/default_padding"
    android:clickable="true"
    android:focusable="true"
    android:src="@drawable/ic_turned_in_not_white_24dp"
    app:layout_anchor="@id/toolbar_container"
    app:layout_anchorGravity="bottom|right|end"
    />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

Here is how I set the paddings in code:

    Insetter.setOnApplyInsetsListener(toolbarContainer, (view, windowInsets, initial) -> {
      view.setPadding(initial.getPaddings().getLeft(),
        windowInsets.getSystemWindowInsetTop() + initial.getPaddings().getTop(),
        initial.getPaddings().getRight(), initial.getPaddings().getBottom());
    });

    Insetter.setOnApplyInsetsListener(cover, (view, windowInsets, initial) -> {
      ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
      params.topMargin = windowInsets.getSystemWindowInsetTop() + initial.getMargins().getTop();
      view.setLayoutParams(params);
    });

    Insetter.setOnApplyInsetsListener(nestedScrollView, (view, windowInsets, initial) -> {
      view.setPadding(initial.getPaddings().getLeft(), initial.getPaddings().getTop(),
        initial.getPaddings().getRight(),
        windowInsets.getSystemWindowInsetBottom() + initial.getPaddings().getBottom());
    });

    Insetter.setOnApplyInsetsListener(favorite, (view, windowInsets, initialPadding) -> {
      view.setPadding(initialPadding.getPaddings().getLeft(), initialPadding.getPaddings().getTop(),
        windowInsets.getSystemWindowInsetRight() + initialPadding.getPaddings().getRight(),
        initialPadding.getPaddings().getBottom());
    });

I set window flags for the fullscreen mode:

root.setSystemUiVisibility(
      View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);

And this is the result pictured:

Example of the incorrect padding

Status bar is set to be transparent, blue color is from the toolbar that has top padding.

As a final result, I would like image to be drawn behind the status bar, is it possible at all?

I'm testing on the Android 10 emulator.

3

There are 3 best solutions below

0
On

As I remember you don't need to support paddings in CoordinatorLayout manually, please see setupForInsets function in CoordinatorLayout class. To support drawing behind status bar you should set android:fitsSystemWindows="true" for CoordinatorLayout:

<androidx.coordinatorlayout.widget.CoordinatorLayout
  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/book_activity_root"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:animateLayoutChanges="true"
  tools:context="com.bookcrossing.mobile.ui.bookpreview.BookActivity"
  >

And as I understand you do not need animateLayoutChanges here. It's responsible for animations in layout changes such as removing/adding and showing/hiding views.

1
On

android:fitsSystemWindows="true"

0
On

Your flags seem to be wrong. You set the flag for the navigation bar while you should be setting the one for the status bar.

Here is the code I use (Kotlin):

requireActivity().window?.decorView?.systemUiVisibility = (
            View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)

to make the UI draw behind status bar without the status bar getting hidden. Then I use this extension function I use on views that should not overlap with the status bar like buttons and other controls:

fun View.alignBelowStatusBar() {
this.setOnApplyWindowInsetsListener { view, insets ->
    val params = view.layoutParams as ViewGroup.MarginLayoutParams
    params.topMargin = insets.systemWindowInsetTop
    view.layoutParams = params
    insets
}

}

So the first flags would ensure your backgrounds/images go under the status bar. The second one is to make sure that your controls are not overlayed by the status bar.