22

I'm struggling with the toolbar and drawer. I'm trying to make the burger switch to arrow when I'm adding a new fragment to the backstack but there is no way to do it.

Maybe I'm missing something but I could not find a way. Anyone had the same problem?

This is the declaration:

mDrawerToggle = new ActionBarDrawerToggle(
            getActivityCompat(),                    /* host Activity */
            mDrawerLayout,                    /* DrawerLayout object */
            ((BaseActivity) getActivityCompat()).getToolbar(),
            R.string.navigation_drawer_open,  /* "open drawer" description for accessibility */
            R.string.navigation_drawer_close  /* "close drawer" description for accessibility */
    )

This is the function I call when a fragment is added to the back stack

public void setToggleState(boolean isEnabled) {
    if (mDrawerLayout == null)
        return;

    if (isEnabled) {
        mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
        mDrawerToggle.onDrawerStateChanged(DrawerLayout.LOCK_MODE_UNLOCKED);
    } else {
        mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
        mDrawerToggle.onDrawerStateChanged(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
    }
    mDrawerToggle.syncState();
}
Marcel
  • 2,094
  • 3
  • 23
  • 37
  • 1
    I am facing the same issue. In my current workaround, i just popup a new Activity with the Fragment and up enabled, where in reality I want to add the Fragment to the backstack and animate the drawer toggle to an up arrow. – Mike T Nov 20 '14 at 07:09

5 Answers5

33

I think all you have to do is to delete the third argument. Thus:

mDrawerToggle = new ActionBarDrawerToggle(
     getActivityCompat(),              /* host Activity */
     mDrawerLayout,                    /* DrawerLayout object */
     // ((BaseActivity) getActivityCompat()).getToolbar(), <== delete this argument
     R.string.navigation_drawer_open,  /* "open drawer" description for accessibility */
     R.string.navigation_drawer_close  /* "close drawer" description for accessibility */
    );

May it helps.

hata
  • 11,633
  • 6
  • 46
  • 69
14

I had the same problem. My solution was this:

Make sure your activity implements onBackStackChangedListener. In your activities' onCreate I set the backstack listener and I set up the toolbar

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

    mFm = getFragmentManager();
    mFm.addOnBackStackChangedListener(this);

    // Setup toolbar
    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    mToolbar.setVisibility(View.VISIBLE);
    setSupportActionBar(mToolbar);

    // Setup drawer toggle
    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerlayout);
    mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.menu_open, R.string.menu_close);
    mDrawerLayout.setDrawerListener(mDrawerToggle);
    getSupportActionBar().setHomeButtonEnabled(true);
    mDrawerToggle.syncState();

    // Setup initial fragment
    if (savedInstanceState == null) {
        mCurrentFragment = DashboardFragment.newInstance();
        mFm.beginTransaction().add(CONTAINER_ID, mCurrentFragment).commit();
    }
}

Also remember to set:

@Override
protected void onPostCreate(Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);
    mDrawerToggle.syncState();
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    mDrawerToggle.onConfigurationChanged(newConfig);
}

Now the magic happens in onBackStackChanged(). Setting the setDrawerIndicatorEnabled to true for the top-most fragment and setDisplayHomeAsUpEnabled as false for that fragment. Inverse for the other ones. I also had to call syncState or else the hamburger wouldn't reappear:

@Override
public void onBackStackChanged() {
    mDrawerToggle.setDrawerIndicatorEnabled(mFm.getBackStackEntryCount() == 0);
    getSupportActionBar().setDisplayHomeAsUpEnabled(mFm.getBackStackEntryCount() > 0);
    mDrawerToggle.syncState();
}
hordurh
  • 2,663
  • 1
  • 16
  • 19
  • Thanks...it worked..Can u tell me how to animate Home Button to Back button...Thanks again – Shashank Kumar Jun 05 '15 at 07:17
  • @ShashankKumar I've been having issues with that myself. You can manually call mDrawerToggle.onDrawerSlide(null, [VALUE]) where value is between 0-1. So to animate you would have to do something like: ValueAnimator anim = ValueAnimator.ofFloat(0, 1); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { mDrawerToggle.onDrawerSlide(null, (float)animation.getAnimatedValue()); } }); anim.setDuration(300); anim.start(); – hordurh Jun 05 '15 at 11:08
  • @ShashankKumar the issue with this though is that the syncState function resets the hamburger state so you need to find a solution to that :). – hordurh Jun 05 '15 at 11:12
  • hi hordurh , try this after your code anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mDrawerToggle.setDrawerIndicatorEnabled(false); getSupportActionBar().setDisplayHomeAsUpEnabled(true); mDrawerToggle.syncState(); } }); anim.setDuration(300); anim.start(); – Shashank Kumar Jun 05 '15 at 13:41
  • above comment did solve the animation problem ...but ican't to the reverse animation i.e. Back>Home...i tried changing ValueAnimator.ofFloat(1, 0); but that didn't work – Shashank Kumar Jun 05 '15 at 13:42
  • This works brilliantly, except that the back button doesn't actually go back. I presume I need to wire that up to pop the fragment. – Keab42 Jul 16 '15 at 11:54
2

I have achieved it using following layout: i have used ActionBarDrawerToggle v7 Drawer.xml

<RelativeLayout 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:orientation="vertical"
    tools:context="com.example.toolbar.Drawer" >

    <include
        android:id="@+id/tool1"
        layout="@layout/toolbar" />



    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/tool1" >

        <FrameLayout
            android:id="@+id/mainContent"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
        </FrameLayout>

        <!-- Nav drawer -->

        <ListView
            android:id="@+id/drawerList"
            android:layout_width="240dp"
            android:layout_height="match_parent"
            android:layout_gravity="left"
            android:background="@android:color/white"
            android:divider="@android:color/white"
            android:dividerHeight="8dp"
            android:drawSelectorOnTop="true"
            android:headerDividersEnabled="true" />
    </android.support.v4.widget.DrawerLayout>

</RelativeLayout>

toolbar.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app1="http://schemas.android.com/apk/res/com.example.toolbar"
    android:id="@+id/my_awesome_toolbar"
    android:layout_width="fill_parent"
    android:layout_height="75dp"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true"
    android:background="?attr/colorPrimary"
    android:minHeight="?attr/actionBarSize"
    app1:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    app1:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" >

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

Drawer.java

package com.example.toolbar;

import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class Drawer extends ActionBarActivity {

    ActionBarDrawerToggle mDrawerToggle;
    private String[] days;
    ArrayAdapter<String> adapter;
    private ListView mDrawerList;
    DrawerLayout mDrawerLayout;

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

        days = new String[] { "sunday", "monday", "tuesday", "wednesday",
                "thursday", "friday", "saturday" };
        adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, days);
        mDrawerList = (ListView) findViewById(R.id.drawerList);
        mDrawerList.setAdapter(adapter);
        mDrawerList.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                // TODO Auto-generated method stub

                Fragment fragment = new MyFragment();
                Bundle args = new Bundle();
                args.putString(MyFragment.ARG_PLANET_NUMBER, days[position]);
                // args.putInt(MyFragment.ARG_PLANET_NUMBER, position);
                fragment.setArguments(args);

                FragmentManager fragmentManager = getSupportFragmentManager();
                fragmentManager.beginTransaction()
                        .replace(R.id.mainContent, fragment).commit();

            }

        });

        Toolbar toolbar = (Toolbar) findViewById(R.id.tool1);

        setSupportActionBar(toolbar);
        toolbar.setTitle("ToolBar Demo");
        toolbar.setLogo(R.drawable.ic_launcher);

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);

        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, toolbar,
                R.string.open_navigation_drawer,
                R.string.close_navigation_drawer) {

            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {
                // TODO Auto-generated method stub
                super.onDrawerSlide(drawerView, slideOffset);
            }

            /** Called when a drawer has settled in a completely closed state. */
            @Override
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                getSupportActionBar().setTitle("hello");
            }

            /** Called when a drawer has settled in a completely open state. */
            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                getSupportActionBar().setTitle("hi");
            }
        };
        mDrawerLayout.setDrawerListener(mDrawerToggle);

        // getSupportActionBar().setDisplayHomeAsUpEnabled(true); //<---- added
        // getSupportActionBar().setHomeButtonEnabled(true); //<---- added

    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) { // <---- added
        if (mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) { // <---- added
        super.onPostCreate(savedInstanceState);
        mDrawerToggle.syncState(); // important statetment for drawer to
                                    // identify
                                    // its state
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) { // <---- added
        super.onConfigurationChanged(newConfig);
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    public void onBackPressed() {
        if (mDrawerLayout.isDrawerOpen(Gravity.START | Gravity.LEFT)) { // <----
                                                                        // added
            mDrawerLayout.closeDrawers();
            return;
        }
        super.onBackPressed();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}
kunal.c
  • 2,739
  • 1
  • 18
  • 24
  • but when you add a new fragment to the backstack does the burger become an arrow? – Marcel Nov 12 '14 at 11:35
  • i haven't tried it yet with stack of fragments but its working fine(burger become an arrow) when i have multiple activities..I think this will work when you add a new fragment . – kunal.c Nov 13 '14 at 05:24
  • What is the parent theme used in the above example in styles.xml? I tried using – Psypher Dec 08 '14 at 18:57
  • I have not used Actionbar instead i have used toolbar – kunal.c Dec 09 '14 at 04:34
2

@hata's answer is spot-on for most cases.

But actually, you were not wrong using toolbar as argument to the ActionBarDrawerToggle(...) constructor.

Of course, there's no sense in doing that if you're using the stock Toolbar AppCompatActivity provides by using, say the stock Theme.AppCompat.Light root theme. Even if you want a custom Toolbar in your layout, e.g. if you're implementing the Material Design's over-toolbar-under-statusbar Navigation Drawer pattern, you can still call setSupportActionBar(toolbar) and still not pass the toolbar to ActionBarDrawerToggle() letting the activity handle the navigation icon.

But if you really want to bind your ActionBarDrawerToggle to a Toolbar that's not activity's action bar, and maybe has different styling and a different icon or whatever -- there's still a way to go.

The thing is, when you don't pass the toolbar as the fourth parameter, the activity is supposed to provide the navigation icon (the arrow) -- and AppCompatActivity returns the value of homeAsUpIndicator attribute -- it's defined in either of the standard AppCompat themes.

However, when you explicitly pass the Toolbar, ActionBarDrawerToggle expects the toolbar instance to provide the navigation indicator drawable -- and it doesn't, because even if you apply the appropriate style to it like this:

<android.support.v7.widget.Toolbar
        ...
        app:theme="?actionBarTheme"
        style="@style/Widget.AppCompat.Toolbar">

for some reason (maybe it's even a bug), the Widget.AppCompat.Toolbar style doesn't have navigationIcon attribute, so Toolbar returns null when ActionBarDrawerToggle asks it for the nav icon. So, to cope with this, you simply derive from the style and add the attribute:

<android.support.v7.widget.Toolbar
        ...
        app:theme="?actionBarTheme"
        style="@style/MyWidget.Toolbar">
<!-- styles.xml -->
<style name="MyWidget.Toolbar" parent="Widget.AppCompat.Toolbar">
    <item name="navigationIcon">?homeAsUpIndicator</item>
</style>

Now you can use the toolbar-aware constructor ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, 0, 0) and still have the navigation icon in place.

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

this line makes sure hamburger icon is changed to arrow on clicking. Make sure you have this written in your code.

mDrawerLayout.setDrawerListener(mDrawerToggle);
isudansh
  • 370
  • 3
  • 16