Android support Toolbar + ActionBarDrawerToggle not changing to arrow

37.5k views Asked by At

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();
}
5

There are 5 answers

1
hata On BEST ANSWER

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.

0
Ivan Bartsov On

@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.

4
kunal.c On

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;
    }

}
6
hordurh On

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();
}
0
isudansh On

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

mDrawerLayout.setDrawerListener(mDrawerToggle);