How to show the shadow of the ActionBar&Toolbar of the support library on all Android versions?

15.8k views Asked by At

This is a simple question:

I use the new support library, which allows me to have a Toolbar instance and even set it as the actionBar of the activity (or use the default one).

How do I customize the shadow being cast by the Toolbar (or the default ActionBar) ?

I've tried to use "setElevation" (on both of them), but it didn't seem to do anything on Android Lollipop.

Also, I can't find how to customize the shadow on pre-Lollipop versions. Is there maybe an API for this? or at least a drawable (I didn't find, even though I've tried) ?


OK, I've managed to find the next things:

For both Lollipop and pre-Lollipop, when your activity uses the normal AppCompat themes (like "Theme.AppCompat.Light" for example) , the shadow should look well.

However, when you use "setSupportActionBar" (and use an appropriate theme, like "Theme.AppCompat.Light.NoActionBar" for example) , you will have some issues with the shadow.

For pre-Lollipop, you can put a FrameLayout below the toolbar, that will look exactly like the one used for the normal themes, as such:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        app:theme="@style/ThemeOverlay.AppCompat.ActionBar" />

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/toolbar"
        android:foreground="?android:windowContentOverlay"
        android:visibility="@integer/action_bar_shadow_background_visibility" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/toolbar"
        android:text="@string/hello_world" />

</RelativeLayout>

You can use the FrameLayout as your content of the activity, or use what I've done. You can also set its visibility by putting integer 0 for pre-lollipop and 2 for Lollipop and above, as I've done with "action_bar_shadow_background_visibility".

It's an ugly solution, but it works, but only for pre-Lollipop versions.

In any case, I still couldn't find a way to show the shadow well for Lollipop, in case I use "setSupportActionBar". I tried using "setElevation" on the toolbar and the actionbar (using "getSupportActionBar", right after calling "setSupportActionBar") .

I also don't get how to get the standard height of the actionbar , as I noticed that a lot of tutorial say to use "minHeight" for the toolbar. Would appreciate help for this too.

Can anyone help me with this?

Again, to make it clear, this is what I ask:

  • for Pre-Lollipop, show the same shadow that's used on the support library for both Toolbar and ActionBar.
  • Same for Lollipop (but use the one that Lollipop has)
  • Extra: customize the shadow to be more "delicate" on Lollipop (and maybe customize it on all versions).
  • Extra: why does the minHeight of the toolbar get a value of the action bar height, instead of the height itself?

EDIT: OK, this is as far as I've done so far to mimic the shadow of Lollipop :

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        app:theme="@style/ThemeOverlay.AppCompat.ActionBar" />

    <include
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/toolbar"
        layout="@layout/toolbar_action_bar_shadow" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/toolbar"
        android:text="@string/hello_world" />

</RelativeLayout>

res/layout/toolbar_action_bar_shadow.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:foreground="?android:windowContentOverlay" />

res/layout-v21/toolbar_action_bar_shadow.xml

<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:src="@drawable/msl__action_bar_shadow" />

res/drawable-v21/res/layout/msl__action_bar_shadow.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <!-- intended for lollipop and above -->

    <item>
        <shape
            android:dither="true"
            android:shape="rectangle" >
            <gradient
                android:angle="270"
                android:endColor="#00000000"
                android:startColor="#33000000" />

            <size android:height="10dp" />
        </shape>
    </item>

</layer-list>

It's not exactly the same, but it's similar.


EDIT: I still don't know why it happens. I think the code I've written on the question is misleading, as all there is to do is to set a background. Yet on the app I'm testing it still doesn't help. The app I'm trying to fix has the toolbar above the navigation drawer.

For a moment I thought that it might be because I've made a customized toolbar (which extends from Toolbar) and that changes its alpha value (according to the navigation drawer), but even if I use the normal toolbar and disable everything that's related to alpha, it still doesn't work. Also, not only that, but on a totally new project, I've tried to set a semi-transpared background for the toolbar, and it got a shadow according to the "setElevation" method.

Now it's even harder for me to find the cause for this problem, because it seems it's not because of transparency and not because of a custom Toolbar class...

Here's the layout of the activity that has the toolbar, in case this can help in any way:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:app="http://schemas.android.com/apk/res-auto"
    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" >

    <android.support.v4.widget.DrawerLayout
        android:id="@+id/activity_main__drawerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

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

        <include
            android:layout_width="240dp"
            android:layout_height="match_parent"
            android:layout_gravity="left"
            android:layout_marginTop="?attr/actionBarSize"
            layout="@layout/activity_main__sliding_menu" />
    </android.support.v4.widget.DrawerLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <com.app.ui.Toolbar
            android:id="@+id/activity_main__toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            android:layoutDirection="ltr"
            android:minHeight="?attr/actionBarSize"
            app:theme="@style/ThemeOverlay.AppCompat.ActionBar"
            tools:ignore="UnusedAttribute" />

        <!-- <include -->
        <!-- android:layout_width="match_parent" -->
        <!-- android:layout_height="wrap_content" -->
        <!-- layout="@layout/toolbar_action_bar_shadow" /> -->
    </LinearLayout>

</FrameLayout>
2

There are 2 answers

7
Jeroen Mols On BEST ANSWER

As far as I know/can read, the AppCompat library currently does not render any shadows. The calls seem to be there (e.g. ViewCompat.setElevation(View view, float elevation)) but it doesn't seem to be doing anything.

So I guess you will need to resort to adding your own shadows for now, at least until the support library implements the shadow rendering.

Looking at the Google IO 2014 source code, they have implemented like this:

  • DrawShadowFrameLayout which can render a background drawable as a shadow
  • bottom_shadow.9.png which represents a shadow below an actionbar
  • app:shadowDrawable attribute to set the shadow drawable
  • setShadowTopOffset() on the DrawShadowFrameLayout shifts the shadow down so it appears nicely below the actionbar. (you need to set this yourself)

More information on this Reddit Thread and stackoverflow.

3
Abhinav Puri On

This is what I have done in one of my apps, it's showing shadow (artificial shadow) :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/mainActivityLinearLayout"
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="match_parent"
        android:background="@drawable/logo">

      <LinearLayout
        android:layout_height="?attr/actionBarSize"
        android:layout_width="match_parent"
        android:orientation="vertical">

        <!-- refer to your toolbar layout -->
        <include layout="@layout/toolbar"/>

      </LinearLayout>

      <!-- My Left Drawer -->
      <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"    >

        <RelativeLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/relative_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginStart="0dp"
            android:layout_marginLeft="0dp" >

           ....

           <!-- Lots of other stuffs -->             

           ....

           <!-- Shadow -->
          <View
           android:layout_width="match_parent"
           android:layout_below="@+id/toolbarShadow"
           android:layout_marginTop="0dp"
           android:layout_height="6dp"
           android:background="@drawable/toolbar_dropshadow" />

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


 </LinearLayout>

toolbar_dropshadow.xml :

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
    <shape>
        <gradient
            android:angle="90"
            android:startColor="@android:color/transparent"
            android:endColor="#D6D6D6"
            android:type="linear" />
    </shape>
</item>
</selector>

I am using a map activity and all this is showing wonderful shadow on my map view.

Hope this helps...