Appcompat v21 Toolbar elevation pre-lollipop

44,741

Solution 1

This worked for me very well:

<android.support.v7.widget.CardView
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/primary"
    card_view:cardElevation="4dp"
    card_view:cardCornerRadius="0dp">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/primary"
        android:minHeight="?attr/actionBarSize" />

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

Solution 2

Using CardView container for toolbar is a bad idea.

CardView is heavy, especially for low end devices.

The best way is to put a gradient Shadow view below the toolbar. Shadow view must be a direct child to the coordinator layout. ie. The appbar which contains toolbar and shadow View must be siblings.

Add this view component to your layout.

 <View
    android:id="@+id/gradientShadow"
    android:layout_width="match_parent"
    android:layout_height="5dp"
    android:background="@drawable/toolbar_shadow"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    app:layout_collapseMode="pin"/>

The drawable toolbar_shadow.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
   android:shape="rectangle">
<gradient
    android:angle="90"
    android:endColor="#33333333"
    android:startColor="@android:color/transparent"/>
</shape>

This will solve the problems in pre-lollipop devices. But we don't want this shadow in lollipop and above devices so make visibility to gone in devices with lollipop and above.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        findViewById(R.id.gradientShadow).setVisibility(View.GONE);
}

Done.

Solution 3

You can add the shadow (elevation) back by using a FrameLayout with foreground="?android:windowContentOverlay". The elevation attribute is not supported pre-Lollipop. So if you are using FrameLayout like fragment container just add foreground attribute to it.

Solution 4

As I've had issues with the CardView widget method, I've used the FrameLayout method as mentioned by @Sniper; it is working perfectly!

I just wanted to share the code snippet you'll have to use. Just put this directly under the toolbar where your main content starts:

<FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:foreground="?android:windowContentOverlay">

And don't forget to close with:

</FrameLayout>

Solution 5

It's possible to have real shadows - animated and generated. The method used by Lollipop is available since Froyo. Hardware acceleration used for shadow generation is available since Honeycomb I guess. Here's how it works:

  • draw your view to an off-screen bitmap with LightingColorFilter set to 0,0
  • blur the black shape (the off-screen bitmap) using the ScriptIntrinsicBlur class and elevation value as radius
  • draw the bitmap beneath the view

It requires adding custom elevation attributes, custom views capable of rendering shadows, and using render script and the compatibility library (for older devices). I'm not going to dive into the details, because there's a lot of them including issues with compilation and minor performance optimisations. But it's possible.

Why there's no shadows in the official support library?

  • it would require changes in the UI framework as it's impossible to freely draw outside view bounds
  • smooth animation requires a quite good GPU

See:

Share:
44,741

Related videos on Youtube

Tristan Vanderaerden
Author by

Tristan Vanderaerden

Updated on March 27, 2020

Comments

  • Tristan Vanderaerden
    Tristan Vanderaerden about 4 years

    First off, I know that this question has been asked before, but it hasn't been answered before. I hope someone can give me an answer.

    In my application, I use the Toolbar from Appcompat_v7 (API 21). This is my code:

    <android.support.v7.widget.Toolbar
        style="@style/DarkActionbarStyle"
        android:id="@+id/toolBar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/actionbar_height" />
    

    And this is the ToolBar style I use:

    <style name="DarkActionbarStyle" parent="@style/Widget.AppCompat.Toolbar">
        <item name="android:background">?attr/colorPrimary</item>
        <item name="titleTextAppearance">@style/ActionBarTitle</item>
        <item name="android:elevation">2dp</item>
        <item name="popupTheme">@style/ThemeOverlay.AppCompat.Light</item>
        <item name="theme">@style/ThemeActionBarDark</item>
    </style>
    
    <style name="ThemeActionBarDark" parent="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
        <item name="actionBarItemBackground">@drawable/btn_dark_orange</item>
        <item name="selectableItemBackground">@drawable/btn_dark_orange</item>
    </style>
    

    The problem is, that elevation doesn't work pre-lollipop. So my question is: Is it possible to have a shadow under the ToolBar on pre-lollipop devices?

  • Tristan Vanderaerden
    Tristan Vanderaerden over 9 years
    This seems to be the only way to add a shadow under the toolbar. But I was secretly hoping for there to be another solution :p
  • Alessandro Roaro
    Alessandro Roaro over 9 years
    Also the CardView implementation in the support api uses a shadow created programmatically to reproduce the shadow
  • Tristan Vanderaerden
    Tristan Vanderaerden about 9 years
    I finally went with this way after testing some of the other answers here. This only works with a frameLayout though, but there are some ways to extend a Relativelayout and use a foreground: gist.github.com/shakalaca/6199283
  • Stoycho Andreev
    Stoycho Andreev about 9 years
    if you have case witch you don't use fragments and FrameLayout then you should make ImageView right under (below) your toolbar and give it shadow drawable like background attribute. For example use @Alessandro Roaro answer and create shadow drawable from this link
  • Tristan Vanderaerden
    Tristan Vanderaerden about 9 years
    Using "?android:windowContentOverlay" everywhere seemed to be more consistent though. I don't like to mix up two approaches. I have Fragments almost everywhere in my layouts, so it's no real problem.
  • link
    link about 9 years
    This is working for me with API 22, but not with API 21 (my minSdk is 19). I'm using the same code as you. Anyway, my application main theme inherits from Theme.AppCompat.Light.NoActionBar. I don't know whether this may be the cause of the bug. Any idea? (I just started android development).
  • Eduardo Lino
    Eduardo Lino over 8 years
    Fixed it. For those having problems with toolbar margins in pre lollipop releases, please refer to this accepted answer: stackoverflow.com/questions/27477371/…
  • Yani2000
    Yani2000 almost 8 years
    How was this answer upvoted? OP asked for pre-lollipop elevation and this answer doesn't elevate the AppBarLayout on pre-lollipop devices. This only adds an elevation effect on v5.0+ devices.
  • Trancer
    Trancer about 7 years
    Solution doesn't work. See imgur.com/dQa6NJj . I think we must override the AppBarLayout to add the shadow to it
  • Trancer
    Trancer about 7 years
    It seems like a solution for this issue. But the shadow does not look very well and realistic. See the picture with Pre-Lollipop and Marshmallow: imgur.com/tyIWMUJ
  • Akexorcist
    Akexorcist almost 6 years
    You can disable elevation in appbar layout in xml to use your gradient all the version. android:stateListAnimator="@null"