How to implement DrawerArrowToggle from Android appcompat v7 21 library

106,540

Solution 1

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.

Solution 2

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.

Solution 3

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>

Solution 4

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.

Solution 5

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

Share:
106,540
BigDX
Author by

BigDX

Updated on September 09, 2020

Comments

  • BigDX
    BigDX over 3 years

    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?

  • BigDX
    BigDX over 9 years
    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.
  • ianhanniballake
    ianhanniballake over 9 years
    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).
  • BigDX
    BigDX over 9 years
    Updated the question if you wouldn't mind taking a look please
  • ianhanniballake
    ianhanniballake over 9 years
    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).
  • BigDX
    BigDX over 9 years
  • mikepenz
    mikepenz over 9 years
    just tried this. but it won't show the DrawerArrowToggle icon for me. Any ideas?
  • Nitin Misra
    Nitin Misra over 9 years
    me also, doesn't show toggle icon, any help appreciated
  • Igoussam
    Igoussam over 9 years
    I have updated the code with the fix from @NitinMisra's answer.
  • BamsBamx
    BamsBamx over 9 years
    With this code the Toolbar gets under the shadow when the navigation drawer is opened. Is this the way its supposed to work?
  • Yong
    Yong over 9 years
    @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.
  • BamsBamx
    BamsBamx over 9 years
    @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
  • mraviator
    mraviator over 9 years
    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?
  • ianhanniballake
    ianhanniballake over 9 years
    @mraviator - the nav drawer and toggle are only part of the support library
  • mraviator
    mraviator over 9 years
    @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?
  • ianhanniballake
    ianhanniballake over 9 years
    @mraviator - yep: change XML, then create and attach the v7 ActionBarDrawerToggle. The canonical answer from the maker of AppCompat has a full example how your XML should be structured.
  • Nitin Misra
    Nitin Misra over 9 years
    @BamsBamx add android:layout_marginTop="?attr/actionBarSize" to root layout of drawer content
  • Ankit Bansal
    Ankit Bansal over 9 years
    Will this animation persist if we use old ActionBarCompat and not the new Toolbar ?
  • aheuermann
    aheuermann over 9 years
    I ran into issues getting the hamburger icon to show up. calling mDrawerToggle.syncState(); fixed it.
  • ChallengeAccepted
    ChallengeAccepted over 9 years
    To use Toolbar as an action bar (cosmetic wise), you want to hide the actionbar by doing getSupportActionBar().hide();
  • Ramesh_D
    Ramesh_D over 9 years
    for me getSupportActionBar() returns null.. what could be the reason ?
  • wzieba
    wzieba over 9 years
    how you use getSupportActionBar() when you just extend Activity?
  • Nitin Misra
    Nitin Misra over 9 years
    simply you can't, you must extend from ActionBarActivity
  • pez
    pez over 9 years
    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?
  • gbhall
    gbhall over 9 years
    @pez Found a solution?
  • Yong
    Yong over 9 years
    @pez Your Activity should extend ActionBarActivity. ActionBarActivity has that method.
  • SweetWisher ツ
    SweetWisher ツ over 9 years
    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 ツ
    SweetWisher ツ over 9 years
    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 ツ
    SweetWisher ツ over 9 years
    I used setSupportActionBar(mToolbar); and also defined <item name="spinBars">true</item> but animation not working
  • Peter Zhao
    Peter Zhao about 9 years
    Should use ActionBarDrawerToggle(Activity, DrawerLayout, int, int) if you are setting the Toolbar as the ActionBar of your activity.
  • Thirumalvalavan
    Thirumalvalavan almost 9 years
    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
    Thirumalvalavan almost 9 years
    setSupportActionBar(toolbar); also commented.
  • WISHY
    WISHY almost 9 years
    hi cannot find drawerArrowStyle in styles. It doest not exist . Using appcompat-v7:22.2.0
  • harsimranb
    harsimranb almost 8 years
    FYI: setDrawerListener is deprecated. You should instead use addDrawerListener().