97

So now that Android 5.0 was released i was wondering how to implement the animated actionbar icons.

This library here implements it fine for me but since the appcompat v7 library has it how can it be implemented?

The library references it in themes.xml

 <item name="drawerArrowStyle">@style/Widget.AppCompat.DrawerArrowToggle</item>

Under this style

 <style name="Base.V7.Theme.AppCompat" parent="Platform.AppCompat">

UPDATE

I got this implemented using the v7 DrawerToggle. However I cannot style it. Please Help

I found the styling for it in the v7 styles_base.xml

<style name="Base.Widget.AppCompat.DrawerArrowToggle" parent="">
    <item name="color">?android:attr/textColorSecondary</item>
    <item name="thickness">2dp</item>
    <item name="barSize">18dp</item>
    <item name="gapBetweenBars">3dp</item>
    <item name="topBottomBarArrowSize">11.31dp</item>
    <item name="middleBarArrowSize">16dp</item>
    <item name="drawableSize">24dp</item>
    <item name="spinBars">true</item>
</style>

I added this to my styles and did not work. Also added to my attr.xml

<declare-styleable name="DrawerArrowToggle">
    <!-- The drawing color for the bars -->
    <attr name="color" format="color"/>
    <!-- Whether bars should rotate or not during transition -->
    <attr name="spinBars" format="boolean"/>
    <!-- The total size of the drawable -->
    <attr name="drawableSize" format="dimension"/>
    <!-- The max gap between the bars when they are parallel to each other -->
    <attr name="gapBetweenBars" format="dimension"/>
    <!-- The size of the top and bottom bars when they merge to the middle bar to form an arrow -->
    <attr name="topBottomBarArrowSize" format="dimension"/>
    <!-- The size of the middle bar when top and bottom bars merge into middle bar to form an arrow -->
    <attr name="middleBarArrowSize" format="dimension"/>
    <!-- The size of the bars when they are parallel to each other -->
    <attr name="barSize" format="dimension"/>
    <!-- The thickness (stroke size) for the bar paint -->
    <attr name="thickness" format="dimension"/>
</declare-styleable>

But crashes and says color type error when doing so. What am i missing?

Ivan Bartsov
  • 19,664
  • 7
  • 61
  • 59
BigDX
  • 3,519
  • 5
  • 38
  • 52

5 Answers5

241

First, you should know now the android.support.v4.app.ActionBarDrawerToggle is deprecated.

You must replace that with android.support.v7.app.ActionBarDrawerToggle.

Here is my example and I use the new Toolbar to replace the ActionBar.

MainActivity.java

public class MainActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(mToolbar);
    DrawerLayout mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    ActionBarDrawerToggle mDrawerToggle = new ActionBarDrawerToggle(
        this,  mDrawerLayout, mToolbar,
        R.string.navigation_drawer_open, R.string.navigation_drawer_close
    );
    mDrawerLayout.setDrawerListener(mDrawerToggle);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    getSupportActionBar().setHomeButtonEnabled(true);
    mDrawerToggle.syncState();
}

styles.xml

<style name="AppTheme" parent="Theme.AppCompat.Light">
    <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
</style>

<style name="DrawerArrowStyle" parent="Widget.AppCompat.DrawerArrowToggle">
    <item name="spinBars">true</item>
    <item name="color">@android:color/white</item>
</style>

You can read the documents on AndroidDocument#DrawerArrowToggle_spinBars

This attribute is the key to implement the menu-to-arrow animation.

public static int DrawerArrowToggle_spinBars

Whether bars should rotate or not during transition
Must be a boolean value, either "true" or "false".

So, you set this: <item name="spinBars">true</item>.

Then the animation can be presented.

Hope this can help you.

Sheychan
  • 2,415
  • 14
  • 32
Yong
  • 2,943
  • 1
  • 13
  • 11
  • just tried this. but it won't show the DrawerArrowToggle icon for me. Any ideas? – mikepenz Oct 21 '14 at 17:23
  • me also, doesn't show toggle icon, any help appreciated – Nitin Misra Oct 22 '14 at 10:01
  • I have updated the code with the fix from @NitinMisra's answer. – Intrications Oct 22 '14 at 16:25
  • With this code the Toolbar gets under the shadow when the navigation drawer is opened. Is this the way its supposed to work? – BamsBamx Oct 23 '14 at 22:03
  • @BamsBamx the Toolbar under the shadow might be result to your layout resouce file. Your Toolbar item might be included in the DrawerLayout so that your Toolbar got under the shadow. – Yong Oct 24 '14 at 02:22
  • @Yong Thanks for the answer. In this case please, could you post an example for the layout? I tried to make a LinearLayout(vertical) with Toolbar(wrap_content) and NavDrawer(weight 1) but, in this case, the NavDrawer wont open if I swipe from the left to the right – BamsBamx Oct 24 '14 at 06:22
  • @BamsBamx add `android:layout_marginTop="?attr/actionBarSize"` to root layout of drawer content – Nitin Misra Oct 29 '14 at 09:28
  • 12
    I ran into issues getting the hamburger icon to show up. calling `mDrawerToggle.syncState();` fixed it. – aheuermann Nov 09 '14 at 05:23
  • To use Toolbar as an action bar (cosmetic wise), you want to hide the actionbar by doing getSupportActionBar().hide(); – ChallengeAccepted Nov 11 '14 at 10:45
  • 1
    for me getSupportActionBar() returns null.. what could be the reason ? – Ramesh_D Dec 09 '14 at 09:50
  • 2
    Android Studio says `Cannot resolve method setSupportActionBar(android.widget.Toolbar)`. I've also tried with `android.support.v7.toolbar`. Does anyone know why this happens? – pez Dec 27 '14 at 05:21
  • @pez Found a solution? – gbhall Dec 27 '14 at 20:06
  • @pez Your Activity should extend ActionBarActivity. ActionBarActivity has that method. – Yong Dec 29 '14 at 15:50
  • I used the same code but i want to hide actionBar i.e. want to use `Theme.AppCompat.Light.NoActionBar` . If I apply this theme, *getSupportActionBar()* will give me error ? how can I achieve this ? @Yong – SweetWisher ツ Jan 07 '15 at 09:39
  • 1
    I used *`setSupportActionBar(mToolbar);`* and also defined `true` but animation not working – SweetWisher ツ Jan 07 '15 at 09:44
  • 2
    Should use ActionBarDrawerToggle(Activity, DrawerLayout, int, int) if you are setting the Toolbar as the ActionBar of your activity. – Peter Zhao Mar 23 '15 at 13:00
  • 1
    hi cannot find drawerArrowStyle in styles. It doest not exist . Using appcompat-v7:22.2.0 – WISHY Jul 06 '15 at 12:18
  • 1
    FYI: setDrawerListener is deprecated. You should instead use `addDrawerListener()`. – harsimranb May 18 '16 at 18:44
24

If you are using the Support Library provided DrawerLayout as suggested in the Creating a navigation drawer training, you can use the newly added android.support.v7.app.ActionBarDrawerToggle (note: different from the now deprecated android.support.v4.app.ActionBarDrawerToggle):

shows a Hamburger icon when drawer is closed and an arrow when drawer is open. It animates between these two states as the drawer opens.

While the training hasn't been updated to take the deprecation/new class into account, you should be able to use it almost exactly the same code - the only difference in implementing it is the constructor.

ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • I am using v4 in my app bc i do not need to support devices prior to api 15. So if i wanted this to work i would have to use the v7 actionbar drawer toggle? If i did that wouldn't I have to convert all my styles to AppCompat and fragment activities to actionbar activities and so on? Or can I implement just the Drawer toggle from v7. Right now I use v7 for card views. Should I even be using v4 if im not supporting api below 15? And I guess I need v7 for the card views. – BigDX Oct 18 '14 at 02:52
  • All of the Support Library compatibility for Material design is implemented in v7-appcompat and is recommended if you want to support that styling on <5.0 devices. AppCompat is built off of and requires v4 so it really up to if you want to use AppCompat at all. If you're already using FragmentActivity, you're already pretty close - the difference in styling is pretty small now (mostly just replacing the android: attributes with non-namespaced attributes). – ianhanniballake Oct 18 '14 at 03:04
  • Updated the question if you wouldn't mind taking a look please – BigDX Oct 18 '14 at 05:00
  • That's really a whole separate question on styling. I'd suggest submitting that part as a whole other question (feel free to add a comment with a link to it). – ianhanniballake Oct 18 '14 at 05:03
  • Thanks http://stackoverflow.com/questions/26439572/how-to-style-the-drawerarrowtoggle-from-android-appcompat-v7-21-library – BigDX Oct 18 '14 at 12:09
  • I'm confused about one thing. If my minSdkVersion is 21 (why not...), do I even need to mess with support libraries for the latest Material Design look nav drawer with hamburger/arrow change? Or is all this built-in to the latest API 21? – mraviator Oct 26 '14 at 14:52
  • @mraviator - the nav drawer and toggle are only part of the support library – ianhanniballake Oct 26 '14 at 15:09
  • @ianhanniballake So I need to use v7 ActionBarDrawerToggle and define my Toolbar in XML in order to include it in the ActionBarDrawerToggle(), as indicate in the answer below by Yong? – mraviator Oct 26 '14 at 15:18
  • 1
    @mraviator - yep: change XML, then create and attach the v7 `ActionBarDrawerToggle`. The [canonical answer](http://stackoverflow.com/a/26440880/1676363) from the maker of AppCompat has a full example how your XML should be structured. – ianhanniballake Oct 26 '14 at 15:36
  • Will this animation persist if we use old ActionBarCompat and not the new Toolbar ? – Ankit Bansal Nov 04 '14 at 13:46
17

I created a small application which had similar functionality

MainActivity

public class MyActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);

        DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer);
        android.support.v7.widget.Toolbar toolbar = (android.support.v7.widget.Toolbar) findViewById(R.id.toolbar);
        ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(
                this,
                drawerLayout,
                toolbar,
                R.string.open,
                R.string.close
        )

        {
            public void onDrawerClosed(View view)
            {
                super.onDrawerClosed(view);
                invalidateOptionsMenu();
                syncState();
            }

            public void onDrawerOpened(View drawerView)
            {
                super.onDrawerOpened(drawerView);
                invalidateOptionsMenu();
                syncState();
            }
        };
        drawerLayout.setDrawerListener(actionBarDrawerToggle);

        //Set the custom toolbar
        if (toolbar != null){
            setSupportActionBar(toolbar);
        }

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        actionBarDrawerToggle.syncState();
    }
}

My XML of that Activity

<android.support.v4.widget.DrawerLayout 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"
    tools:context=".MyActivity"
    android:id="@+id/drawer"
    >

    <!-- The main content view -->
    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <include layout="@layout/toolbar_custom"/>
    </FrameLayout>
    <!-- The navigation drawer -->
    <ListView
        android:layout_marginTop="?attr/actionBarSize"
        android:id="@+id/left_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp"
        android:background="#457C50"/>


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

My Custom Toolbar XML

<?xml version="1.0" encoding="utf-8"?>

<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/toolbar"
    android:background="?attr/colorPrimaryDark">
    <TextView android:text="U titel"
        android:textAppearance="@android:style/TextAppearance.Theme"
        android:textColor="@android:color/white"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
</android.support.v7.widget.Toolbar>

My Theme Style

<resources>
    <style name="AppTheme" parent="Base.Theme.AppCompat"/>

    <style name="AppTheme.Base" parent="Theme.AppCompat">
        <item name="colorPrimary">@color/primary</item>
        <item name="colorPrimaryDark">@color/primaryDarker</item>
        <item name="android:windowNoTitle">true</item>
        <item name="windowActionBar">false</item>
        <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
    </style>

    <style name="DrawerArrowStyle" parent="Widget.AppCompat.DrawerArrowToggle">
        <item name="spinBars">true</item>
        <item name="color">@android:color/white</item>
    </style>

    <color name="primary">#457C50</color>
    <color name="primaryDarker">#580C0C</color>
</resources>

My Styles in values-v21

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme" parent="AppTheme.Base">
        <item name="android:windowContentTransitions">true</item>
        <item name="android:windowAllowEnterTransitionOverlap">true</item>
        <item name="android:windowAllowReturnTransitionOverlap">true</item>
        <item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
        <item name="android:windowSharedElementExitTransition">@android:transition/move</item>
    </style>
</resources>
timr
  • 6,668
  • 7
  • 47
  • 79
  • 1
    This works perfect but I don't want to use Title so how can I handle this ? Because if i use *Theme.AppCompat.Light.NoActionBar*, it will definitely give me NULL at *getSupportActionBar* – SweetWisher ツ Jan 07 '15 at 07:43
  • I used above code. I got Exception: This Activity already has an action bar supplied by the window decor in setsupportactionbar(toolbar) line. I used getSupportActionBar().hide(); Before setContentView(R.layout.activity_main); Now it is working. – Thirumalvalavan May 29 '15 at 06:44
  • setSupportActionBar(toolbar); also commented. – Thirumalvalavan May 29 '15 at 06:52
9

To answer the updated part of your question: to style the drawer icon/arrow, you have two options:

Style the arrow itself

To do this, override drawerArrowStyle in your theme like so:

<style name="AppBaseTheme" parent="Theme.AppCompat.Light">
    <item name="drawerArrowStyle">@style/MyTheme.DrawerArrowToggle</item>
</style>
<style name="MyTheme.DrawerArrowToggle" parent="Widget.AppCompat.DrawerArrowToggle">
    <item name="color">@android:color/holo_purple</item>
    <!-- ^ this will make the icon purple -->
</style>

This is probably not what you want, because the ActionBar itself should have consistent styling with the arrow, so, most probably, you want the option two:

Theme the ActionBar/Toolbar

Override the android:actionBarTheme (actionBarTheme for appcompat) attribute of the global application theme with your own theme (which you probably should derive from ThemeOverlay.Material.ActionBar/ThemeOverlay.AppCompat.ActionBar) like so:

<style name="AppBaseTheme" parent="Theme.AppCompat.Light">
    <item name="actionBarTheme">@style/MyTheme.ActionBar</item>
</style>
<style name="MyTheme.ActionBar" parent="ThemeOverlay.AppCompat.ActionBar">
    <item name="android:textColorPrimary">@android:color/white</item>
    <!-- ^ this will make text and arrow white -->
    <!-- you can also override drawerArrowStyle here -->
</style>

An important note here is that when using a custom layout with a Toolbar instead of stock ActionBar implementation (e.g. if you're using the DrawerLayout-NavigationView-Toolbar combo to achieve the Material-style drawer effect where it's visible under translucent statusbar), the actionBarTheme attribute is obviosly not picked up automatically (because it's meant to be taken care of by the AppCompatActivity for the default ActionBar), so for your custom Toolbar don't forget to apply your theme manually:

<!--inside your custom layout with DrawerLayout
and NavigationView or whatever -->
<android.support.v7.widget.Toolbar
        ...
        app:theme="?actionBarTheme">

-- this will resolve to either AppCompat's default ThemeOverlay.AppCompat.ActionBar or your override if you set the attribute in your derived theme.

PS a little comment about the drawerArrowStyle override and the spinBars attribute -- which a lot of sources suggest should be set to true to get the drawer/arrow animation. Thing is, spinBars it is true by default in AppCompat (check out the Base.Widget.AppCompat.DrawerArrowToggle.Common style), you don't have to override actionBarTheme at all to get the animation working. You get the animation even if you do override it and set the attribute to false, it's just a different, less twirly animation. The important thing here is to use ActionBarDrawerToggle, it's what pulls in the fancy animated drawable.

Ivan Bartsov
  • 19,664
  • 7
  • 61
  • 59
2

I want to correct little bit the above code

    public class MainActivity extends ActionBarActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar);
        DrawerLayout mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle mDrawerToggle = new ActionBarDrawerToggle(
            this,  mDrawerLayout, mToolbar,
            R.string.navigation_drawer_open, R.string.navigation_drawer_close
        );
        mDrawerLayout.setDrawerListener(mDrawerToggle);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);
    }

and all the other things will remain same...

For those who are having problem Drawerlayout overlaying toolbar

add android:layout_marginTop="?attr/actionBarSize" to root layout of drawer content

Nitin Misra
  • 4,472
  • 3
  • 34
  • 52