Case

Let’s consider Toolbar that is not child of the AppBarLayout and it is overlayed over the AppBarLayout. We expect everything to slide below the Toolbar. If the Toolbar is child of the AppBarLayout it has to be the first child amongst the other. Being first at this hierarchy, it has to have layout_scrollFlag, so the siblings can move up when AppBarLayout offsets receives the nested scroll events. Our intention is to keep the Toolbar on the top and be able to sweep everything else underneath.

Toolbar being top child (activity_main.xml, few attributes are trimmed):

<merge xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto">



<android.support.design.widget.CoordinatorLayout

....>



<android.support.design.widget.AppBarLayout

...

android:elevation="@dimen/appbar_elevation">





<android.support.design.widget.TabLayout

android:id="@+id/tabs"

android:layout_marginTop="?attr/actionBarSize"

android:layout_width="match_parent"

android:layout_height="?attr/actionBarSize"

app:layout_scrollFlags="scroll|exitUntilCollapsed" />

</android.support.design.widget.AppBarLayout> <android.support.v4.view.ViewPager

...

app:layout_behavior="@string/appbar_scrolling_view_behavior">

</android.support.design.widget.CoordinatorLayout>



<android.support.v7.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:theme="@style/ThemeOverlay.AppCompat">

</android.support.v7.widget.Toolbar>

</merge>

Nope, the FrameLayout is not missing, with <merge> tag we are merging it the android.R.id.content FrameLayout that will take care of the z-ordering and place the Toolbar where we intended.

Blending in the Toolbar should be rather cosmetics and easy as putting background same as AppBarLayout. Because our Toolbar is top child on the z-order, its elevation is visible and it has to be set to zero, so it blends in completely.

Toolbar is missing elevation

Cool! Everything works as described, AppBarLayout offsets children as it should and Toolbar is there. But hey…we lost the elevation!

We can make a use of AppBarLayout and its cool OnOffsetChangedListener. This listener is triggered when vertical offset is changed, i.e bottom and top are offset. Parameter `verticalOffset` is always between 0 and -mAppbarLayout.getTotalScrollRange(), which is the total height of all children that can scroll (have set scrollFlags in their layout params).

We will need to start elevating the Toolbar as soon as the absolute offset is greater than the difference between AppBarLayout scroll range and Toolbar’s height, in code:

verticalOffset = Math.abs(verticalOffset);

int difference = appBarLayout.getTotalScrollRange() - mToolbar.getHeight(); if (verticalOffset >= difference) {

/* start elevating gradually to our target elevation

}

Tip: verticalOffset values are always <= 0, get rid off the negative sign, first thing!

Elevation

Target elevation should be considered the maximum between the one we have defined and the target elevation of the AppBarLayout. In the layout I have assigned the same value.

mTargetElevation = Math.max(mTargetElevation, appBarLayout.getTargetElevation());

Gradual elevation is controlled by the tiny space we are left and the current offset until we reach completely collapsed AppbarLayout:

float flexibleSpace = appBarLayout.getTotalScrollRange() - verticalOffset;

float ratio = 1 - (flexibleSpace / mToolbar.getHeight());

float elevation = ratio * appBarLayout.getElevation();

Boom! We have the elevation derived! Now, all we need is to set to the Toolbar:

private void setToolbarElevation(float targetElevation) {

ActionBar supportActionBar = mActivity.getSupportActionBar();

if (supportActionBar != null)

supportActionBar.setElevation(targetElevation);

else

ViewCompat.setElevation(mToolbar, targetElevation);

}

Usage:

mAppBarLayout.addOnOffsetChangedListener(new ToolbarElevationOffsetListener(this, mToolbar));

Demo:

Full code:

The gist is available here.

Improvements:

This can be achieved with CoordinatorLayout.Behavior too, which I might discuss in next post.