Add elevation/shadow on toolbar for pre-lollipop devices
Asked Answered
P

8

43

I updated my android app to the new material design, but I also wanted to add some shadow or elevation to the Toolbar. There seem to be some (hacky) ways of doing it via images/9-patches, but I wonder if it can be done via the support libraries. (just like the CardView can have elevation)

According to this answer on another question, this is possible by wrapping the Toolbar in a AppBarLayout, but this doesn't work for me.

My layout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="wrap_content">
    <android.support.v7.widget.Toolbar
            android:id="@+id/Toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.AppBarLayout>

I also tried setting elevation via XML and through code, but that doesn't work either.

Any help would be appreciated! Thanks in advance.

Update:

Since I include my Toolbar layout in my other layouts, below is one of my main layouts:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <include
        layout="@layout/Toolbar" />
    <fragment
        class="OverAllField.XamarinAndroid.Fragments.Planning.PlanningFragment"
        android:id="@+id/PlanningFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>
Pleasurable answered 29/6, 2015 at 12:18 Comment(3)
Are you using <include> tags to include it in your activity layout? If yes, post your activity layout.Emmi
@BinoyBabu Yes, I use include tags. I updated my answer with the activity layout. Thanks for your time.Pleasurable
refer my answer..https://mcmap.net/q/138643/-android-appcompat-21-elevationBrewton
H
69

For Android 5.0 and above : AppBarLayout automatically provides/gives shadow in the layout. You can also increase the elevation of the AppBarLayout by app:elevation="4dp".

For Pre-Lollipop : You can use the following link: https://github.com/vipulasri/Toolbar-Elevation-Pre-Lollipop

Note: Toolbar also supports elevation to it, using android:elevation="4dp"


New Update: In Appcompat v24.0.0, you can not set elevation to AppBarLayout using setElevation() and app:elevation as these are deprecated.

You have to use stateListAnimator property to set elevation now.

Note: set duration to 1ms in StateListAnimator in order to avoid delay in Elevation Drawing.

AppBarLayout elevation change is delayed on appCompat v24.0.0

appbar_always_elevated.xml in animator-v21 folder under res directory.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
    <objectAnimator android:propertyName="elevation"
                    android:valueTo="8dp" 
                    android:valueType="floatType"
                    android:duration="1"/>
</item>
</selector>

In AppbarLayout :

<android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:fitsSystemWindows="true"
        android:stateListAnimator="@animator/appbar_always_elevated"
        android:theme="@style/AppTheme.AppBarOverlay">

</android.support.design.widget.AppBarLayout>
Hetti answered 4/9, 2015 at 8:39 Comment(8)
Adding app:elevation doesn't work for me on pre-Lollipop, whether I add it to the AppBarLayout or to the ToolbarChromite
@Chromite in that case you have to use second option.Hetti
That link was unbelievably awesome. +1Straightforward
@VipulAsri, the link (blog.grafixartist.com/add-a-toolbar-elevation-on-pre-lollipop) is broken.Antoneantonella
The link isn't very useful if you apply this to the toolbar on an activity that has a list. The "shadow" completely hides the items when you start to scroll down and completely displaces highlight color at the top when you "overscroll" to the top. Not good.Seizing
Thanks man!, stateListAnimator completely working from 21+, To use it programmatically do this: appBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator(context, R.animator.appbar_always_elevated));.Lenwood
This answer is not solve the issue with shadow. It adds more problems only. See imgur.com/UrFLioZRedfish
@Redfish that was your error, the toolbar and the shadow should not share the same parent, it also happened to me.Cole
E
7

Try using the AppBarLayout inside the activity layout. Try:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <include
            layout="@layout/Toolbar" />
    </android.support.design.widget.AppBarLayout>

    <fragment
        class="OverAllField.XamarinAndroid.Fragments.Planning.PlanningFragment"
        android:id="@+id/PlanningFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>

Toolbar.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/Toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
Emmi answered 29/6, 2015 at 13:14 Comment(4)
That doesn't show shadow either in my case. I am using a phone with android 4.4.4 KitKat, if that helps. ThanksPleasurable
I'm at a loss then. I think you might have to use drop shadow in the fragment foreground for pre lollipop.Emmi
Ok, weird that it works for you. Maybe it has to do with me using Xamarin, and it only works in the native support libary... I'll look at the other solutions then. Thanks for your help anyways, appreciate it.Pleasurable
@avb1994 I have the same issue and am using Xamarin as well. So it may well be a Xamarin issue. :-(Tawana
O
2

use build file:


compile 'com.android.support:cardview-v7:23.1.1'

refer this link

to call in xml add:

app:cardElevation="8dp"
app:cardCornerRadius="8dp"
app:contentPadding="5dp"
Otilia answered 1/7, 2016 at 6:22 Comment(0)
M
2

I think the best solution is to put a gradient shadow view below the toolbar and manipulate with visibility depends on device sdk.

toolbar.xml

 <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@android:color/white"
            android:fitsSystemWindows="true"
            app:popupTheme="@style/AppTheme.PopupOverlay">

            <TextView
                android:id="@+id/centerTitleToolbarTextView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:maxLines="1"
                android:textColor="@color/color_toolbar"
                android:textSize="@dimen/titleToolbar" />

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

        <View
            android:id="@+id/shadow_view"
            android:layout_width="match_parent"
            android:visibility="gone"
            android:layout_height="4dp"
            android:background="@drawable/toolbar_shadow" />

    </LinearLayout>

toolbar_shadow.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient
        android:startColor="#c1c1c1"
        android:centerColor="#e6e6e6"
        android:endColor="#f1f1f1"
        android:angle="270" />
</shape>

MainActivity.class

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        findViewById(R.id.shadow_view).setVisibility(View.VISIBLE);
      }
Mutule answered 12/1, 2018 at 16:2 Comment(0)
F
2

I have a CollapsingToolbarLayout with Toolbar and a Toolbar-like View that moves up and down, but lays above NestedScrollView, like in https://github.com/k0shk0sh/CoordinatorLayoutExample. I tried many variants. Sometimes a shadow scrolled above a screen with NestedScrollView, sometimes the Toolbar drew a solid shadow without transparency, sometimes the shadow was aliased. Anyway, this is my solution.

Say, you have a layout:

<android.support.design.widget.CoordinatorLayout>
    <android.support.design.widget.AppBarLayout>
         <android.support.design.widget.CollapsingToolbarLayout>
             <android.support.v7.widget.Toolbar>
                 <!-- Toolbar views if needed -->
             </android.support.v7.widget.Toolbar>
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <!-- Footer Toolbar views if needed -->

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout>
            <!-- Views -->
        </LinearLayout>

    </android.support.v4.widget.NestedScrollView>

    <!-- Add this view. It draws a shadow and aligns top of NestedScrollView -->
    <!-- Set visibility to gone or visible -->
    <View
        android:id="@+id/scroll_shadow"
        android:layout_width="match_parent"
        android:layout_height="4dp"
        android:background="@drawable/shadow"
        android:visibility="gone"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

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

Add a shadow (drawable/shadow.xml):

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient
        android:angle="90"
        android:endColor="#ffcccccc"
        android:startColor="#00cccccc" />
</shape>

Add this method. Here scrollShadow is a view named "scroll_shadow":

private void setShadowVisibility(int visibility) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        scrollShadow.setVisibility(visibility);
    }
}

Manipulate it as you wish. It will show a gradient on pre-Lollipop devices and shows a normal shadow for Android 5.0+.

Fireball answered 30/1, 2018 at 16:34 Comment(1)
Tried this solution, it is suitable for RecyclerView and NestedScrollView, for still and moving Toolbar.Fireball
A
1

I have also lost 1 day on this issue, but I finally figured out a way to make it work.

/**
 * Controller for the toolbar which will take care of the drop down shadow also on pre-lollipop devices.
 * <br/>
 * The controller also handles the status bar based on the state of the toolbar.
 * <p/>
 * Created by <b>Negru Ionut Valentin</b> on <b>20/1/2016</b>.
 */
public class ToolbarController {

    private boolean handleStatusBar = false;
    private boolean showTitle = true;

    /**
     * Call this method in onCreate() method of your Activity or in onCreateView() in Fragment
     *
     * @param view
     *         The view into which to look for the toolbar
     * @param activity
     *         The activity for which setup the toolbar
     *
     * @return The toolbar found and customized or {@code null}
     */
    public Toolbar initToolbar(View view, Activity activity) {
        Toolbar mToolbar = (Toolbar) view.findViewById(R.id.toolbar);

        // Valid and visible toolbar - otherwise ignore
        if (null != mToolbar && mToolbar.getVisibility() == View.VISIBLE) {

            int paddingLeft = mToolbar.getPaddingLeft();
            int paddingRight = mToolbar.getPaddingRight();
            int paddingBottom = mToolbar.getPaddingBottom();
            // Set the top padding of the toolbar to match the status bar height
            int paddingTop = new CynnyContextWrapper(activity).getStatusBarHeight();

            mToolbar.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);

            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
                ViewParent parent = mToolbar.getParent();
                if (parent instanceof RelativeLayout) {
                    // Manually create the drop shadow
                    RelativeLayout.LayoutParams params =
                            new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                                            Metrics.convertDpToPixel(4, activity));

                    View dropShadow = new View(activity);
                    dropShadow.setBackgroundResource(R.drawable.toolbar_shadow);

                    params.addRule(RelativeLayout.BELOW, R.id.toolbar);

                    ((RelativeLayout) parent).addView(dropShadow, params);
                }
            }

            if (activity instanceof AppCompatActivity) {
                // Check if the Activity actually support ActionBar with Toolbar and set our custom Toolbar for it
                ((AppCompatActivity) activity).setSupportActionBar(mToolbar);

                // Get the actionbar from the activity
                ActionBar actionBar = ((AppCompatActivity) activity).getSupportActionBar();
                if (null != actionBar) {
                    // If the actionbar is valid, customize it
                    actionBar.setDisplayHomeAsUpEnabled(true);
                    actionBar.setHomeButtonEnabled(true);

                    actionBar.setDisplayShowTitleEnabled(this.showTitle);

                    mToolbar.setNavigationIcon(R.drawable.ic_arrow_back_selector);
                }
            }

            if (this.handleStatusBar) {
                // For showing the status bar
                activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
                activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
            }
        } else if (handleStatusBar) {
            // Force hide the status bar
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
        }

        return mToolbar;
    }

    /**
     * Set the flag indicating if the controller should handle or not the status bar.
     *
     * @param handleStatusBar
     *         Flag which indicates if the initialization of the toolbar should also update
     *         the status bar state (if two calls are made for the init, the last one will
     *         be taken into consideration).
     *
     * @return The current toolbar controller.
     */
    public ToolbarController setHandleStatusBar(boolean handleStatusBar) {
        this.handleStatusBar = handleStatusBar;

        return this;
    }

    /**
     * Set the flag indicating if the toolbar should show or hide the title.
     *
     * @param showTitle
     *         Flag indicating if the toolbar should also show the title (the title can be changed after the toolbar
     *         initialization)
     *
     * @return The current toolbar controller.
     */
    public ToolbarController setShowTitle(boolean showTitle) {
        this.showTitle = showTitle;

        return this;
    }
}

In the activity you should use this in onCreate() method like this:

// Create and customize the Toolbar controller
new ToolbarController().setHandleStatusBar(true).setShowTitle(true)
                                 .initToolbar(((ViewGroup) findViewById(android.R.id.content)).getChildAt(0),
                                              this);

In the fragment you should use this in onCreateView() method like this:

new ToolbarController().setHandleStatusBar(false).setShowTitle(false).initToolbar(resultView, getActivity());

Do not forget to add you toolbar in the layouts and set it's id with android:id="@id/toolbar". If you want to use another id, you can customize the controller and add another setter method which uses the id you provide.

I have two toolbar layouts created:

v21

    <!-- This is the new widget added in Lollipop - use with care -->
    <android.support.v7.widget.Toolbar
            android:id="@id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="?attr/actionBarSize"
            android:theme="@style/TitleToolbarTheme"
            android:background="?android:attr/colorPrimary"
            android:elevation="@dimen/toolbar_elevation"
            />
</merge>

other

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

    <!-- This is the new widget added in Lollipop - use with care -->
    <android.support.v7.widget.Toolbar
            android:id="@id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="?attr/actionBarSize"
            android:theme="@style/TitleToolbarTheme"
            android:background="?attr/colorPrimary"
            />

</merge>

I use them in may layouts using:

<include layout="@layout/toolbar" />

I hope this will help to solve your issue. Also take note this can be optimized further more, but it works for me and I don't want to loose anymore time on this issue.

Alainealair answered 20/1, 2016 at 15:23 Comment(2)
What is toolbar_shadow you have in the code example.Modestomodesty
It is an 9patch drawable resource.Alainealair
A
1

A simple solution, when using CoordinatorLayout, is to anchor an ImageView with the "shadow" just below the AppBarLayout, or Toolbar. This even works with CollapsingToolbarLayout, and it's correctly rendered atop the "content":

<CoordinatorLayout>
  <AppBarLayout android:id="@+id/app_bar">
    <Toolbar/>
  </AppBarLayout>
  <include layout="@layout/app_bar_shadow"/>
  <!-- your content usually goes here -->
</CoordinatorLayout>

Create a res/layout/app_bar_shadow.xml file, which will only be use on pre-Lollipop devices:

<ImageView
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:adjustViewBounds="true"
  app:layout_anchor="@+id/app_bar"
  app:layout_anchorGravity="bottom"
  android:layout_gravity="bottom"
  app:srcCompat="@drawable/shadow_app_bar"
  />

Create an "empty" res/layout-v21/app_bar_shadow.xml file, used for Android 5+ devices:

 <merge/>

Finally, an appropriate res/drawable/shadow_app_bar.xml:

<shape>
  <gradient
    android:angle="90"
    android:startColor="#00000000"
    android:endColor="#30000000"
    />
  <size android:height="@dimen/design_appbar_elevation" />
</shape>
Allspice answered 5/10, 2019 at 0:5 Comment(0)
C
0

Use CardView

   <android.support.v7.widget.CardView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="top"
    app:cardCornerRadius="0dp">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">


        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:popupTheme="@style/AppTheme.PopupOverlay" />
    </android.support.design.widget.AppBarLayout>
</android.support.v7.widget.CardView>
Calla answered 28/4, 2017 at 13:34 Comment(3)
Hey Rina, can you please explain what is the change you are suggesting? why would it solve the issue?Micturition
to elevate appbar in pre-lolipop device surround it with cardview and elevate cardview as per need. this will elevate appbar and shadow will be given automatically @MicturitionCalla
That's like the worst solution I've ever seen.Fortieth

© 2022 - 2024 — McMap. All rights reserved.