1

I am trying to set up proper navigation in my application, which replaces Fragments in a main content area, so I have only one single Activity. I have one main Fragment and several subFragments, for example a Fragment for preferences. Everything works fine when using the back button, but I want to implement the up navigation including icon in addition to this. I am using the ActionBar fetched with Activity.getSupportActionBar() together with a Toolbar from appcompat and an ActionBarDrawerToggle.

I followed this tutorial when setting up my Drawer in the first place.

Current behavior: When I start the app, the list/drawer icon is shown in the left part of the ActionBar. When I click this, the Drawer opens and I can select items. Sub Fragments are replaced into my content and the back button pops the stack, taking me back to the previous Fragment.

Missing behavior: The list/drawer icon in the top left is never replaced by the back arrow icon and I cannot figure out how to implement this properly. The Drawer is always pulled out when clicking the list/drawer icon, no matter which Fragment I am in.

What have I tried:

  1. I tried following this answer. It kinda works, meaning that the back arrow icon is set in the sub Fragments, but clicking the back arrow still opens the Drawer instead of providing up navigation. Also, when using the back button to go "up", the list/drawer icon is replaced by nothing.
  2. I also tried following this answer. Here, the desired ActionBar behavior/look is implemented in the onCreate() method of the various Fragments. Using this I could get the back arrow up, but still the Drawer is pulled when clicking the arrow.
  3. Various other minor things and hacks.

My questions:

  1. What is wrong in my code below?
  2. Is it correct/normal to use the combination ActionBar, Toolbar and ActionBarDrawerToggle to implement the Drawer navigation together with up navigation?

MyActivity.onCreate():

@Override
protected void onCreate(Bundle savedInstanceState)
{
    // Other stuff

    // Setup drawer.
    mDrawerFragment = (DrawerFragment)
            getSupportFragmentManager().findFragmentById(R.id.mm_navigation_drawer);
    mDrawerFragment.initialize(this, (DrawerLayout)findViewById(R.id.mm_drawer_layout), toolbar);
}

DrawerFragment class

public class DrawerFragment extends Fragment
{
    private MyActivity                      mMyActivity;
    private MyActionBarDrawerToggle         mMyBarDrawerToggle;
    private DrawerLayout                    mDrawerLayout;
    private FragmentDrawerListener          mFragmentDrawerListener;
    private View                            mContainerView;

    public void initialize(MyActivity myActivity, final DrawerLayout drawerLayout, final Toolbar toolbar)
    {
        mMyActivity = myActivity;
        mFragmentDrawerListener = mMyActivity;
        mContainerView = myActivity.findViewById(R.id.mm_navigation_drawer);

        mMyActionBarDrawerToggle = new MyActionBarDrawerToggle(myActivity, drawerLayout, toolbar, R.string.mm_drawer_open, R.string.mm_drawer_close);

        mDrawerLayout = drawerLayout;
        mDrawerLayout.setDrawerListener(mMyActionBarDrawerToggle);
        mDrawerLayout.post(new Runnable()
        {
            @Override
            public void run()
            {
                mMyActionBarDrawerToggle.syncState();
            }
        });
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState)
    {
        // Not relevant, just create and return the View.
    }
}

MyActivity.onDrawerItemSelected()

The implementation of the interface FragmentDrawerListener is done in the MyActivity class. It simply replaces the content area with other Fragments, using FragmentTransactions.

@Override
public void onDrawerItemSelected(View view, int postion)
{
    switch (postion)
    {
        case DrawerAdapter.ITEM_FILTERED_RECIPES:
            showFilteredRecipesFragment();
            break;

        case DrawerAdapter.ITEM_SELECTED_RECIPES:
            showSelectedRecipesFragment();
            break;

        case DrawerAdapter.ITEM_SHOPPING_LIST:
            showShoppingListFragment();
            break;

        case DrawerAdapter.ITEM_SETTINGS:
            showSettingsFragment();
            break;

        case DrawerAdapter.ITEM_ABOUT:
            showAboutFragment();
            break;
    }
}

MyActionBarDrawerToggle class

public class MyActionBarDrawerToggle extends ActionBarDrawerToggle
{
    private MyActivity mMyActivity;
    private Toolbar mToolbar;

    public MyActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, Toolbar toolbar, int openDrawerContentDescRes, int closeDrawerContentDescRes)
    {
        super(activity, drawerLayout, toolbar, openDrawerContentDescRes, closeDrawerContentDescRes);
        mMyActivity = (MyActivity) activity;
        mToolbar = toolbar;
    }

    @Override
    public void onDrawerOpened(View drawerView)
    {
        super.onDrawerOpened(drawerView);
        mMyActivity.invalidateOptionsMenu();
    }

    @Override
    public void onDrawerClosed(View drawerView)
    {
        super.onDrawerClosed(drawerView);
        mMyActivity.invalidateOptionsMenu();
    }

    @Override
    public void onDrawerSlide(View drawerView, float slideOffset)
    {
        super.onDrawerSlide(drawerView, slideOffset);
        mToolbar.setAlpha(1 - slideOffset / 2);
    }
}

The DrawerFragment is inflated in the main layout using a simple, static Fragment instance like this:

<fragment
    android:id="@+id/my_navigation_drawer"
    android:name="com.my.company.gui.drawer.DrawerFragment"
    android:layout_width="@dimen/my_nav_drawer_width"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:layout="@layout/my_drawer_navigation_fragment"
    tools:layout="@layout/my_drawer_navigation_fragment">
</fragment>
Community
  • 1
  • 1
Krøllebølle
  • 2,878
  • 6
  • 54
  • 79

1 Answers1

1

If you're using material design for you app, then it is expected that you use the Toolbar to replace the Actionbar in your activity. You will still maintain your ActionBarDrawerToggle and functionality will remain the same.

As for your fragments, as long as they are 'housed' by the same activtiy (i.e the activity with the drawer), changing fragments will not cause the drawer toggle to change the back arrow. It will only change if you navigate to a new activity. Only then will the main activity be treated as home and with the other activities having a back arrow to navigate back

Alex Kombo
  • 3,256
  • 8
  • 34
  • 67
  • So what you are saying is that such navigation using the `Toolbar` is designed to use `Activity`s for replacing contents, not `Fragment`s? – Krøllebølle Sep 02 '15 at 13:40
  • Navigation using the `Toolbar` is between activities. If you want to implement back navigation between fragments within the same activty, you will have to override the `onBackPressed` method. – Alex Kombo Sep 02 '15 at 13:54
  • Ok, so I probably have to rewrite some `Fragment`s to `Activity`s. Is the preferred way to do this to have the same `Toolbar` in all XML files, get it in `Activity.onCreate()` and thereafter call `setSupportActionBar(Toolbar)`? I guess also `getSupportActionBar().setDisplayHomeAsUpEnabled(true);` has to be called? – Krøllebølle Sep 02 '15 at 15:00
  • Simplify things by creating the toolbar on a different xml file then add it using the `` tag in every activity where you need it to appear. And yes, you need to `setSupportActionBar(toolbar)` in each of the activities and also add `getSupportActionBar().setDisplayAsHomeUpEnabled(true)` for each of them so that the correct navigation occurs. – Alex Kombo Sep 02 '15 at 15:04
  • It seems to work ok now, except that when pressing the up navigation button in my child `Activity`, `onDestroy()` is called directly in my main `Activity` followed by `onCreate()`. Is this the expected behavior? As far as I can tell from the documentation, `onDestroy()` should not be called at that point. Pressing the back button provides the desired behavior; I just get returned to the main `Activity` with state restored. Do you by chance have any knowledge of this? – Krøllebølle Sep 02 '15 at 17:08
  • Just for reference, the order of calling is: 1) Press button for opening child `Activity`, 2) `onPause()` in main `Activity` is called, 3) `onSaveInstanceState()` in main `Activity` is called, 4) child `Activity` is active with up navigation, wait as long as you like, 5) Press up navigation in child `Activity`, 6) `onDestroy()` in main `Activity` is called. Note that I have set up the main `Activity` as the parent `Activity` of the child in `AndroidManifest.xml`. – Krøllebølle Sep 02 '15 at 17:16
  • So this last issue seemingly had to do with not calling `finish()` when the child `Activity` performed up navigation. I added the `onOptionItemSelected` as shown in [this answer](http://stackoverflow.com/a/16672997/1139324). – Krøllebølle Sep 03 '15 at 11:30