15

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>
android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • Possible duplicate of [No shadow by default on Toolbar?](http://stackoverflow.com/questions/26575197/no-shadow-by-default-on-toolbar) – rds Sep 21 '16 at 08:27

2 Answers2

14

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.

David Passmore
  • 6,089
  • 4
  • 46
  • 70
Jeroen Mols
  • 3,436
  • 17
  • 24
  • Why did they use "DrawShadowFrameLayout" ? do they hide the shadow on some cases? – android developer Dec 23 '14 at 07:46
  • Yes indeed, they choose the shadow drawable based on a reference in values/refs.xml and this reference is @null for api level 21. – Jeroen Mols Dec 23 '14 at 08:36
  • I meant, why did they use this class? It has an option to hide the shadow... They could just set the visibility/drawable based on the resource. – android developer Dec 23 '14 at 09:13
  • Not sure why they use the `setShadowVisible` method, but I would guess it has to do with hiding the shadow after a scroll... Using `DrawShadowFrameLayout` has two advantages: 1. they can use exactly the same layouts for lollipop as for older api's (`DrawShadowFrameLayout` neatly hides all implementation details) 2. this class can potentially be used for more types of shadow (ex. cardview -> side + bottom shadows) by just injecting a different 9-patch – Jeroen Mols Dec 23 '14 at 09:38
  • I see. I still don't get how come on my tiny tests everything seem to work fine, yet on the app they don't. In any case, so your suggestion is to use a drawable and that's it, which is what I did in the workaround, right? – android developer Dec 23 '14 at 12:43
  • Indeed, that's the only option we seem to have. The Google IO app does the same, and @chrisbanes also confirmed this on twitter. – Jeroen Mols Dec 23 '14 at 15:26
  • Too bad. Can you please show me the link to what you've talked about (of "chrisbanes") ? – android developer Dec 23 '14 at 20:24
3

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...

Abhinav Puri
  • 4,254
  • 1
  • 16
  • 28
  • Well, I did the same on the code I've posted. I was hoping to see how to use the normal one on Lollipop, and to know what causes it to not appear well for some reason. – android developer Dec 20 '14 at 20:35
  • For some reason (probably a bug), if you are using set elevation on lollipop devices, then make sure that you donot set a solid color with a transparency, eg: #eeff0000, the elevation shadow does not show up, instead use : #ff0000 for correct usage. Also, background color with alpha doesn't seems to work either. Use background color without alpha. If still your shadow doesn't work as expected, then post your code for lollipop which you are using, I might be able to help. Hope this helps... – Abhinav Puri Dec 20 '14 at 21:05
  • See my edit. Sadly that's not it, and on a new project, I've actually succeeded using a background that's semi transparent. Now it's even harder for me to find the cause for it. – android developer Dec 21 '14 at 07:46