Manage toolbar's navigation and back button from fragment in android

AndroidAndroid ActionbarNavigation DrawerAndroid FragmentactivityAndroid Toolbar

Android Problem Overview


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

Android Solutions


Solution 1 - Android

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

Solution 2 - Android

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");

Solution 3 - Android

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

Solution 4 - Android

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.

Solution 5 - Android

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

Solution 6 - Android

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.

Solution 7 - Android

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

Solution 8 - Android

(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)

Solution 9 - Android

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.

Solution 10 - Android

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"/>

Solution 11 - Android

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" />

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAbhishek DhimanView Question on Stackoverflow
Solution 1 - AndroidVladimir PetrovskiView Answer on Stackoverflow
Solution 2 - AndroidAmandeep RohilaView Answer on Stackoverflow
Solution 3 - AndroidGeekView Answer on Stackoverflow
Solution 4 - AndroidAppyxView Answer on Stackoverflow
Solution 5 - AndroidIshan FernandoView Answer on Stackoverflow
Solution 6 - AndroidMohankumar ShanView Answer on Stackoverflow
Solution 7 - AndroidKyaw San OoView Answer on Stackoverflow
Solution 8 - AndroidMatt GrierView Answer on Stackoverflow
Solution 9 - AndroidLorenzoView Answer on Stackoverflow
Solution 10 - AndroidMudit GoelView Answer on Stackoverflow
Solution 11 - AndroidDeepak GuptaView Answer on Stackoverflow