62

All of my fragments are controlled through ActionBarActivity (mainActivity), inside mainActivity a DrawerLayout is implemented and all the child fragments are pushed through drawerLayout's list item click. The problem that I'm facing is after pushing a fragment through drawerLayout I want to change the drawer icon into back icon of ToolBar so that user can navigate to previous fragment and to handle the callback of android.R.id.home either inside the same fragment or inside the mainActivity.

The code that I am using is:

MainActivity.java

public class MainActivity extends ActionBarActivity {
    private DrawerLayout layoutDrawer;
    private ActionBarDrawerToggle drawerToggler;
    private Stack<Fragment> stack;

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        stack = new Stack<Fragment>();
        layoutDrawer = (DrawerLayout) findViewById(R.id.layout_drawer);
        drawerToggler = new ActionBarDrawerToggle(this, layoutDrawer, toolbar,
                R.string.app_name, R.string.app_name);
        layoutDrawer.setDrawerListener(drawerToggler);

        setUpDrawerList();
        pushFragment(new FirstFragment(), true);

        Session.setContext(getApplicationContext());
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (drawerToggler.isDrawerIndicatorEnabled()
                && drawerToggler.onOptionsItemSelected(item))
            return true;
        switch (item.getItemId()) {
        case android.R.id.home:
            Toast.makeText(this, "Back from activity", Toast.LENGTH_SHORT)
                    .show();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

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

    @Override
    public void onBackPressed() {
        popFragment();
    }

    private void setUpDrawerList() {
        ListView listView = (ListView) findViewById(R.id.list_drawer);
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1,
                Arrays.asList(new String[] { "First Fragment",
                        "Second Fragment" }));
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                layoutDrawer.closeDrawers();
                drawerToggler.setDrawerIndicatorEnabled(false);
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);
                pushFragment(getFragment(position), true);
            }
        });
    }

    private Fragment getFragment(int pos) {
        switch (pos) {
        case 0:
            return new FirstFragment();
        case 1:
            return new SecondFragment();
        }
        return null;
    }

    public void pushFragment(Fragment fragment, boolean add) {
        FragmentTransaction transation = getSupportFragmentManager()
                .beginTransaction();
        if (add)
            stack.push(fragment);
        transation.replace(R.id.layout_content, fragment);
        transation.commit();
    }

    public void popFragment() {
        if (!stack.isEmpty()) {
            Fragment fragment = stack.elementAt(stack.size() - 2);
            stack.pop();
            pushFragment(fragment, false);
        } else
            super.onBackPressed();
        drawerToggler.setDrawerIndicatorEnabled(stack.size() == 1);
    }

    public void clearBackStack() {
        stack.clear();
    }
}

FirstFragment.java

public class FirstFragment extends Fragment {

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_first, container, false);
    }

    @Override
    public void onResume() {
        super.onResume();
        ActionBar actionBar = ((ActionBarActivity)getActivity()).getSupportActionBar();
        actionBar.setTitle("First Fragment");
        actionBar.setDisplayHomeAsUpEnabled(true);
        actionBar.setHomeButtonEnabled(true);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        menu.clear();
        inflater.inflate(R.menu.fragment_menu, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()) {
        case android.R.id.home:
            Toast.makeText(getActivity(), "Back from fragment", Toast.LENGTH_SHORT).show();
            getActivity().onBackPressed();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

From the above code I am not able to get the callback of android.R.id.home and setting home button is not working everytime actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true);

Any help will be really appreciated.

Thanks

Abhishek Dhiman
  • 1,631
  • 6
  • 25
  • 38
  • 1
    https://github.com/tekinarslan/AndroidMaterialDesignToolbar in this you can get the same behaviour which you want – Deepak Gupta Sep 08 '15 at 11:02

11 Answers11

76

Add a toolbar to your xml

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Fragment title"/>

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

Then inside your onCreateView method in the Fragment:

Toolbar toolbar = view.findViewById(R.id.toolbar);
toolbar.setNavigationIcon(R.drawable.ic_back_button);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
         getActivity().onBackPressed();
    }
});
Michael
  • 9,639
  • 3
  • 64
  • 69
Vladimir Petrovski
  • 1,966
  • 21
  • 25
39

You have to manage your back button pressed action on your main Activity because your main Activity is container for your fragment.

First, add your all fragment to transaction.addToBackStack(null) and now navigation back button call will be going on main activity. I hope following code will help you...

@Override
    public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case android.R.id.home:
        onBackPressed();
        }
    return super.onOptionsItemSelected(item);
}

you can also use

Fragment fragment =fragmentManager.findFragmentByTag(Constant.TAG); 
if(fragment!=null) {          
      FragmentTransaction transaction = fragmentManager.beginTransaction();
      transaction.remove(fragment).commit();
}

And to change the title according to fragment name from fragment you can use the following code:

activity.getSupportActionBar().setTitle("Keyword Report Detail");
Tausif Shahbaz
  • 164
  • 2
  • 15
Amandeep Rohila
  • 3,558
  • 2
  • 28
  • 34
  • 2
    Beware that you need to call `return true` after `onBackPressed()` for this to work. If you don't `onBackPressed()` will have no effect as the call will be consumed by `super.onOptionsItemSelected(item)` – hanspeide Jan 03 '17 at 12:51
29

I have head around lots of solutions and none of them works perfectly. I've used variation of solutions available in my project which is here as below. Please use this code inside class where you are initialising toolbar and drawer layout.

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
                drawerFragment.mDrawerToggle.setDrawerIndicatorEnabled(false);
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);// show back button
                toolbar.setNavigationOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        onBackPressed();
                    }
                });
            } else {
                //show hamburger
                drawerFragment.mDrawerToggle.setDrawerIndicatorEnabled(true);
                getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                drawerFragment.mDrawerToggle.syncState();
                toolbar.setNavigationOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        drawerFragment.mDrawerLayout.openDrawer(GravityCompat.START);
                    }
                });
            }
        }
    });
Geek
  • 1,510
  • 14
  • 17
  • 1
    After trying couple of solution finally it worked for me. – Sharad Jun 14 '17 at 09:19
  • This works fine, I added a line at the end of show hamburguer else ( drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); ) and another at the end of the (if (getSupportFragmentManager().getBackStackEntryCount() > 0) ) which is ( drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);) so the user cannot open the drawer by dragging when the back arrow is shown – Setar Jul 03 '17 at 18:21
  • 2
    This is so much unclear... what is `drawerFragment`, what is `mDrawerToggle`? Egregious example of a bad answer – jujka Oct 06 '17 at 12:11
  • 2
    @GeorgySavatkov, this is answer to question, not tutorial. Please don't look at it from tutorial perspective. – Geek Oct 07 '17 at 08:46
  • 1
    This worked for me. I have a FragmentActivity implementing AppCompatCallback. I removed the navigation drawer and hamburger icon part because I didn't need it but this works great. – Sithe Jun 26 '19 at 23:06
  • the best solution i find it, work like charm you are a great geek (y) – zakaria Jan 28 '20 at 11:20
21

Probably the cleanest solution:

abstract class NavigationChildFragment : Fragment() {

    abstract fun onCreateChildView(inflater: LayoutInflater,
                                   container: ViewGroup?,
                                   savedInstanceState: Bundle?): View?

    override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val activity = activity as? MainActivity
        activity?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
        setHasOptionsMenu(true)
        return onCreateChildView(inflater, container, savedInstanceState)
    }

    override fun onDestroyView() {
        val activity = activity as? MainActivity
        activity?.supportActionBar?.setDisplayHomeAsUpEnabled(false)
        setHasOptionsMenu(false)
        super.onDestroyView()
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        val activity = activity as? MainActivity
        return when (item.itemId) {
            android.R.id.home -> {
                activity?.onBackPressed()
                true
            }
            else              -> super.onOptionsItemSelected(item)
        }
    }
}

Just use this class as parent for all Fragments that should support navigation.

Appyx
  • 1,145
  • 1
  • 12
  • 21
10

You can use Toolbar inside the fragment and it is easy to handle. First add Toolbar to layout of the fragment

<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:fitsSystemWindows="true"
    android:minHeight="?attr/actionBarSize"
    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    android:background="?attr/colorPrimaryDark">
</android.support.v7.widget.Toolbar>

Inside the onCreateView Method in the fragment you can handle the toolbar like this.

 Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
 toolbar.setTitle("Title");
 toolbar.setNavigationIcon(R.drawable.ic_arrow_back);

IT will set the toolbar,title and the back arrow navigation to toolbar.You can set any icon to setNavigationIcon method.

If you need to trigger any event when click toolbar navigation icon you can use this.

 toolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
           //handle any click event
    });

If your activity have navigation drawer you may need to open that when click the navigation back button. you can open that drawer like this.

 toolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            DrawerLayout drawer = (DrawerLayout) getActivity().findViewById(R.id.drawer_layout);
            drawer.openDrawer(Gravity.START);
        }
    });

Full code is here

 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    //inflate the layout to the fragement
    view = inflater.inflate(R.layout.layout_user,container,false);

    //initialize the toolbar
    Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
    toolbar.setTitle("Title");
    toolbar.setNavigationIcon(R.drawable.ic_arrow_back);
    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //open navigation drawer when click navigation back button
            DrawerLayout drawer = (DrawerLayout) getActivity().findViewById(R.id.drawer_layout);
            drawer.openDrawer(Gravity.START);
        }
    });
    return view;
}
Ishan Fernando
  • 2,758
  • 1
  • 31
  • 38
  • your `R.drawable.ic_arrow_back` is a custom drawable, do you know whether it's possible to grab the "official" arrow that Android uses ? – Someone Somewhere Apr 19 '18 at 15:50
8

First you add the Navigation back button

   getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    getSupportActionBar().setDisplayShowHomeEnabled(true);

Then, add the Method in your HostActivity.

 @Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId()==android.R.id.home)
    {
        super.onBackPressed();
        Toast.makeText(this, "OnBAckPressed Works", Toast.LENGTH_SHORT).show();
    }

    return super.onOptionsItemSelected(item);
}

Try this, definitely work.

Mohankumar Shan
  • 131
  • 1
  • 3
3

if you are using androidx fragment and want to go back to MainActivity when back home button is clicked, use the following codes .

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    setHasOptionsMenu(true);
    ....
}


@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == android.R.id.home) {
        requireActivity().onBackPressed();
    }
    return super.onOptionsItemSelected(item);
}
Kyaw San Oo
  • 157
  • 1
  • 7
2

(Kotlin) In the activity hosting the fragment(s):

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
    when (item.itemId) {
        android.R.id.home -> {
            onBackPressed()
            return true
        }
    }
    return super.onOptionsItemSelected(item)
}

I have found that when I add fragments to a project, they show the action bar home button by default, to remove/disable it put this in onViewCreated() (use true to enable it if it is not showing):

val actionBar = this.requireActivity().actionBar
    actionBar?.setDisplayHomeAsUpEnabled(false)
Matt Grier
  • 206
  • 2
  • 9
0

The easiest solution I found was to simply put that in your fragment :

androidx.appcompat.widget.Toolbar toolbar = getActivity().findViewById(R.id.toolbar);
    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            NavController navController = Navigation.findNavController(getActivity(), 
R.id.nav_host_fragment);
            navController.navigate(R.id.action_position_to_destination);
        }
    });

Personnaly I wanted to go to another page but of course you can replace the 2 lines in the onClick method by the action you want to perform.

Lorenzo
  • 17
  • 2
0

Generic method in Kotlin which can handle both the title bar back press action as well as navigation bar back press action and can be called from all the fragments is :

fun configureToolbarBackPress(
            customToolBar: Toolbar,
            parentVw: View,
            activity: Activity,
            title: String,
            targetResId: Int
        )    {
            customToolBar.setNavigationIcon(R.drawable.ic_arrow_back)
            customToolBar.title = title
            customToolBar.setNavigationOnClickListener {
                parentVw.findNavController().navigate(targetResId)
            }

            (activity as DashboardActivityNew).onBackPressedDispatcher.addCallback(
                object : OnBackPressedCallback(true) {
                    override fun handleOnBackPressed() {
                        parentVw.findNavController().navigate(targetResId)
                    }
                }
            )
        }

Now call this method onViewCreated() from any of the fragment as follows :

AppUtils.configureToolbarBackPress(
            customToolbar as Toolbar,
            view,
            requireActivity(),
            getString(R.string.title),
            R.id.mainFragment
        )

Include the toolbar in fragment.xml as follows :

<include
            android:id="@+id/customToolbar"
            layout="@layout/dashboard_toolbar" /> 

The layout of dashboard_toolbar.xml is as follows :

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="@dimen/abc_action_bar_default_height_material"
    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    android:background="@color/colorPrimary"/>
Mudit Goel
  • 196
  • 1
  • 5
-2

OnToolBar there is a navigation icon at left side

Toolbar  toolbar = (Toolbar) findViewById(R.id.tool_bar);
        toolbar.setTitle(getResources().getString(R.string.title_activity_select_event));
        setSupportActionBar(toolbar);

        getSupportActionBar().setDisplayShowHomeEnabled(true);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

By using this at left side navigation icon appear and on navigation icon click it call parent activity.

and in manifest we can notify system about parent activity.

  <activity
            android:name=".CategoryCloudSelectActivity"
            android:parentActivityName=".EventSelectionActivity"
            android:screenOrientation="portrait" />
Deepak Gupta
  • 979
  • 1
  • 10
  • 18