CoordinatorLayout and AppBarLayout elevation
Asked Answered
P

4

22

I've created an AppBar layout like this

<android.support.design.widget.AppBarLayout
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/appbar_layout"
    android:layout_height="@dimen/app_bar_height"
    android:layout_width="match_parent"
    android:fitsSystemWindows="true"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:elevation="20dp">

    <android.support.design.widget.CollapsingToolbarLayout...>
</android.support.design.widget.AppBarLayout>

it works and casts a shadow in the LinearLayout:

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

    <include layout="@layout/app_bar_large" />
</LinearLayout>

However when I put it into the CoordinatorLayout shadow is gone:

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include layout="@layout/app_bar_large" />
</android.support.design.widget.CoordinatorLayout>

How can I make appbar to show its shadow again?

enter image description here

Percept answered 7/12, 2015 at 17:9 Comment(0)
K
18

This is actually an implementation detail of CollapsingToolbarLayout, as seen in the source code:

if (Math.abs(verticalOffset) == scrollRange) {
  // If we have some pinned children, and we're offset to only show those views,
  // we want to be elevate
  ViewCompat.setElevation(layout, layout.getTargetElevation());
} else {
  // Otherwise, we're inline with the content
  ViewCompat.setElevation(layout, 0f);
}

Which removes the elevation when the CollapsingToolbarLayout is showing non-pinned elements - by default, it'll only have elevation when only pinned children are visible.

Kreiker answered 7/12, 2015 at 19:41 Comment(3)
I wonder why this answer got many upvotes. What if the author has no pinned elements?Illdisposed
@LeoDroidcoder - the vast majority of CollapsingToolbarLayout usages have a pinned Toolbar within it, hence the name. You probably don't want or need to use CollapsingToolbarLayout at all if that isn't the case.Kreiker
There might be a pinned Toolbar which will let to draw a shadow in the collapsed state of CollapsingToolbarLayout. But what many developers want is to draw the shadow in an expanded state as well, i.e. with any value of verticalOffset. Even though it might contradict with the Material Design guidelinesIlldisposed
P
9

the reason is above,try this to solve:

appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
            //some other code here
            ViewCompat.setElevation(appBarLayout, The Elevation In Px);
        }
    });
Pith answered 5/7, 2017 at 7:17 Comment(1)
Works inconsistently on Android 10 with the latest AndroidX lib : sometimes the elevation is the one you asked for, sometimes it's the default one (when scrolling in the NestedView) and sometimes it's simply gone (when the CollapsingToolbarLayout is in expanded state).Story
U
5

The solution is to use app:elevation=0dp to remove the default elevation and set android:translationZ to the elevation you want.

Note : The code below uses the latest AndroidX / Material libraries and might not work if you're using the old support library

<com.google.android.material.appbar.AppBarLayout
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:translationZ="8dp"
    app:elevation="0dp">

    <!--
      * `app:elevation=0dp` disables the default shadow that is automatically added on
      scroll ; other values e.g. `6dp` are ignored despite what the official doc says
      (see below)
      * so instead we're using `android:translationZ` to add a shadow with a custom
      elevation
    -->

The documentation for AppBarLayout # setTargetElevation() states that you can set a custom elevation value using the app:elevation attribute, but it didn't work for me for values greater than 0dp, so I'm using translationZ as a workaround.

Unconscionable answered 16/3, 2020 at 18:24 Comment(1)
Yes, elevation is too crazy. Should remove it and use translationZ instead. ThanksPopsicle
Y
1

setTargetElevation() is now deprecated for AppBarLayout.

The new correct implementation for applying custom elevation to an AppBarLayout based on the state of the layout is to use a StateListAnimator. material-components uses this as you can see here

I've added an example implementation of always showing AppBarLayout elevation here in this gist.

All you need is to 1. create a custom state list animator under /res/animator and 2. set the AppBarLayout's StateListAnimator like so:

appBarLayout.stateListAnimator = AnimatorInflater.loadStateListAnimator(context, R.animator.appbar_always_elevated_state_list_animator)
Yvoneyvonne answered 1/6, 2020 at 21:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.