In BottomNavigationView it is possible to set:
bottomNavigationView.setOnNavigationItemReselectedListener(item -> {
// do nothing on reselection
});
However for NavigationView this is not available. What is a good equivalent?
UPDATE
Thanks to #ande I implemented the following:
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
if (currentMenuItemId == item.getItemId()) {
navDrawer.close();
return true;
}
currentMenuItemId = item.getItemId();
NavigationUI.onNavDestinationSelected(item, navController);
navDrawer.close();
return true;
}
That works well for if I only navigate via the menu items. (Btw, I just implemented the Listener in my Activity and added it from there, no need for an extra class)
However, when I press the back button then I will be able to press the menu button for the current destination, as the menu item did not update on onBackPressed().
Update 2
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
#Override
public void onDestinationChanged(#NonNull NavController controller, #NonNull NavDestination destination, #Nullable Bundle arguments) {
currentMenuItemId = destination.getId();
}
});
This solved it!
May be you can do like this ( I did quickly so may be it could be better!!)
class ReSelectedListener(val navigationViewCallBack: NavigationViewCallBack) : NavigationView.OnNavigationItemSelectedListener {
var newItemSelected : MenuItem? = null
override fun onNavigationItemSelected(item: MenuItem): Boolean {
if(newItemSelected != null){
if(newItemSelected!!.itemId == item.itemId){
navigationViewCallBack.setOnNavigationItemReselectedListener(item)
}
}
newItemSelected = item
return true
}
}
interface NavigationViewCallBack {
fun setOnNavigationItemReselectedListener(item: MenuItem)
}
And after
class YourFragment :Fragment(), NavigationViewCallBack
and
navigationView.setNavigationItemSelectedListener(ReSelectedListener(this))
and implements callback method in YourFragment:
override fun setOnNavigationItemReselectedListener(item: MenuItem) {
TODO("Not yet implemented")
}
Related
After OnCreateOptionsMenu() marked as deprecated, I've managed to use new API from release notes https://developer.android.com/jetpack/androidx/releases/activity#1.4.0-alpha01
In my app user can switch Fragments via bottomNavigation.
As I understand docs, in each Fragment I've implemented MenuProvider(with or without Lifecycle, doesn't matter for result). But now in each Fragment user have all items from all menuInflaters.
There is the code of implementation
FRAGMENT A
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
SetMainParams();
fragment = inflater.inflate( R.layout.fragment_A, container, false );
addMenu();
return fragment;
}
private void addMenu()
{
MenuProvider menuProvider = new MenuProvider()
{
#Override
public void onCreateMenu(#NonNull Menu menu, #NonNull MenuInflater menuInflater)
{
menuInflater.inflate(R.menu.menu_fragment_A, menu);
}
#Override
public boolean onMenuItemSelected(#NonNull MenuItem menuItem)
{
if( menuItem.getItemId() == R.id.filters_prev )
filtersPrevious();
else if( menuItem.getItemId() == R.id.filters )
showFilters();
else
filtersNext();
return false;
}
};
requireActivity().addMenuProvider(menuProvider, getViewLifecycleOwner(), Lifecycle.State.RESUMED);
}
FRAGMENT B
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
SetMainParams();
binding = FragmentBBinding.inflate(inflater, container, false);
fragment = binding.getRoot();
init();
addMenu();
return fragment;
}
private void addMenu()
{
MenuProvider menuProvider = new MenuProvider()
{
#Override
public void onCreateMenu(#NonNull Menu menu, #NonNull MenuInflater menuInflater)
{
menuInflater.inflate(R.menu.menu_fragment_B, menu);
filtersMenu = menu.getItem(0);
}
#Override
public boolean onMenuItemSelected(#NonNull MenuItem menuItem)
{
if( menuItem.getItemId() == R.id.filters )
loadFilters();
return false;
}
};
requireActivity().addMenuProvider(menuProvider, getViewLifecycleOwner(), Lifecycle.State.RESUMED);
}
Switching from bottomNavigation
binding.bottomNav.setOnItemSelectedListener(item ->
{
int itemId = item.getItemId();
if( itemId == R.id.A )
{
fm.beginTransaction().hide(active_fragment).show(A_fragment).commit();
active_fragment = A_fragment;
setWithElevation(false);
}
else if( itemId == R.id.B )
{
fm.beginTransaction().hide(active_fragment).show(B_fragment).commit();
active_fragment = B_fragment;
setWithElevation(true);
}
active_fragment.startFragment();
active_fragment.setTitle();
return true;
});
fm.beginTransaction().add( R.id.fl_content, A_fragment, "A_fragment" ).hide(A_fragment).commit();
fm.beginTransaction().add( R.id.fl_content, B_fragment, "B_fragment" ).hide(B_fragment).commit();
Is there any ideas, why new API works like this, or maybe i've made a mistake. Thanks a lot for help :)
I also experienced this problem today. So not sure if you have intellisense, but look at some of the Menu interface methods. I used the hasVisibleItems() method in Kotlin to only inflate the menu if it does not exist. Java also has the method; your snippet augmented with conditional:
MenuProvider menuProvider = new MenuProvider()
{
#Override
public void onCreateMenu(#NonNull Menu menu, #NonNull MenuInflater menuInflater)
{
if (!menu.hasVisibleItem()) { // only inflate if no items
menuInflater.inflate(R.menu.menu_fragment_A, menu);
}
}
//...
Menu also has a bunch of other methods that may assist for specific items.
Also another good idea would be to use the MenuHost interface to keep track of your MenuProvider. I believe in Java; inside your Fragments; it would be along the lines of:
activityFragment = this.requireActivity();
Menuhost menuHost = (MenuHost) activityFragment;
menuHost.addMenuProvider(
menuProvider,
this.getViewLifecycleOwner(),
State.RESUMED
);
Menu providers can then be invalidated ect.
I've a similar problem. In my application, I'm using fragments and navigation. The problem I found is that during navigation, the fragment never call the onPause event. So all my fragment implements this code in onViewCreated method:
class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
...
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(HomeMenuProvider(R.id.nav_main, findNavController(), this, authViewModel, homeViewModel), viewLifecycleOwner, Lifecycle.State.RESUMED)
}
The HomeMenuProvider is so defined:
class HomeMenuProvider(private val currentNavId: Int) : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
if ((navController.currentDestination?.id ?: -1L) == currentNavId) {
menu.clear()
menuInflater.inflate(R.menu.menu_main, menu)
}
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.menu_action_login -> {
...
true
}
else -> {
false
}
}
}
}
As you can see in my code, I add menu items only if the current navigation status is the one associated with the fragment. Otherwise, simply I do nothing.
To avoid menu persistence, before add items I clear the menu.
I hope this helps.
How do I declare a menu inside of Android fragment? The method that I had used previously is now deprecated.
Originally:
override fun onCreateView(...): View {
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
this.menu = menu
inflater.inflate(R.menu.menu, this.menu)
}
From the Developer documentation, this can be achieved by the following:
/**
* Using the addMenuProvider() API directly in your Activity
**/
class ExampleActivity : ComponentActivity(R.layout.activity_example) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Add menu items without overriding methods in the Activity
addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
// Add menu items here
menuInflater.inflate(R.menu.example_menu, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
// Handle the menu selection
return true
}
})
}
}
/**
* Using the addMenuProvider() API in a Fragment
**/
class ExampleFragment : Fragment(R.layout.fragment_example) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// The usage of an interface lets you inject your own implementation
val menuHost: MenuHost = requireActivity()
// Add menu items without using the Fragment Menu APIs
// Note how we can tie the MenuProvider to the viewLifecycleOwner
// and an optional Lifecycle.State (here, RESUMED) to indicate when
// the menu should be visible
menuHost.addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
// Add menu items here
menuInflater.inflate(R.menu.example_menu, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
// Handle the menu selection
return when (menuItem.itemId) {
R.id.menu_clear -> {
// clearCompletedTasks()
true
}
R.id.menu_refresh -> {
// loadTasks(true)
true
}
else -> false
}
}
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
Fragments setHasOptionsMenu deprecated, use setHasOptionsMenu
Expanding on what #joseph-wambura and #hammad-zafar-bawara said, you can also implement the interface in the fragment...
class MyFragment : Fragment(), MenuProvider {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Do stuff...
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.options, menu)
// Do stuff...
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
// Do stuff...
return false
}
}
In Kotlin, declaration for Activity, Fragment and PreferenceFragmentCompat
Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.main_menu, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
// Handle the menu selection
return true
}
})
}
}
Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// The usage of an interface lets you inject your own implementation
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
// Add menu items here
menuInflater.inflate(R.menu.main_menu, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
// Handle the menu selection
return when (menuItem.itemId) {
R.id.action_menu1 -> {
// todo menu1
true
}
R.id.action_menu2 -> {
// todo menu2
true
}
else -> false
}
}
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
PreferenceFragmentCompat
val menuHost: MenuHost = requireHost() as MenuHost
//Same declaration with Fragment
Use MenuProvider interface
class FirstFragment : Fragment(), MenuProvider {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
// Add menu items here
menuInflater.inflate(R.menu.second_menu, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
// Handle the menu selection
return when (menuItem.itemId) {
R.id.menu_clear -> {
// Do stuff...
true
}
R.id.menu_refresh -> {
// Do stuff...
true
}
else -> false
}
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
JAVA CODE
Option menu in Activity
addMenuProvider(new MenuProvider() {
#Override
public void onCreateMenu(#NonNull Menu menu, #NonNull MenuInflater menuInflater) {
menuInflater.inflate(R.menu.bottom_nav_menu, menu);
// Add menu options here
}
#Override
public boolean onMenuItemSelected(#NonNull MenuItem menuItem) {
// Handle Menu Options Selection Here
return false;
}
});
Option menu in Fragment
requireActivity().addMenuProvider(new MenuProvider() {
#Override
public void onCreateMenu(#NonNull Menu menu, #NonNull MenuInflater menuInflater) {
menuInflater.inflate(R.menu.bottom_nav_menu, menu);
// Add option Menu Here
}
#Override
public boolean onMenuItemSelected(#NonNull MenuItem menuItem) {
return false;
// Handle option Menu Here
}
}, viewLifecycleOwner, Lifecycle.State.RESUMED);
If you're using Jetpack NavigationUI then you need to setSupportActionBar(toolbar), otherwise the menu will not be present.
this helps me on onCreateView Method:
requireActivity().addMenuProvider(new MenuProvider() {
#Override
public void onCreateMenu(#NonNull Menu menu, #NonNull MenuInflater menuInflater) {
menuInflater.inflate(R.menu.bottom_nav_menu, menu);
// Add option Menu Here
}
#Override
public boolean onMenuItemSelected(#NonNull MenuItem menuItem) {
// Handle option Menu Here
return false;
}
}, getViewLifecycleOwner, Lifecycle.State.RESUMED);
I'm not sure why all answers recommend passing in Lifecycle.State.RESUME as the lifecycle state to the addMenuProvider call. This will make the menu disappear when the fragment is paused, which doesn't look great when the fragment is paused and still visible.
For example, showing a dialog as a result of tapping a menu item will make the menu disappear. It will re-appear when the dialog is dismissed.
For most cases, a better lifecycle state to pass in would be Lifecycle.State.CREATE, which will only remove the menu when the view is destroyed. This is also the default behaviour, so you can simply omit the lifecycle state.
in java for fragment i tried this . it works fine for me
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_example, container, false);
toolbar = (Toolbar)view.findViewById(R.id.toolbar_1);
((AppCompatActivity)getActivity()).setSupportActionBar(toolbar);
MenuHost menuHost = requireActivity();
menuHost.addMenuProvider(new MenuProvider() {
#Override
public void onCreateMenu(#NonNull Menu menu, #NonNull MenuInflater menuInflater) {
menuInflater.inflate(R.menu.menu_search,menu);
}
#Override
public boolean onMenuItemSelected(#NonNull MenuItem menuItem) {
if (menuItem.getItemId() == R.id.search_friend){
Toast.makeText(getActivity(), "friends", Toast.LENGTH_SHORT).show();
return true;
}
else return false;
}
},getViewLifecycleOwner(), Lifecycle.State.RESUMED);
return view;
}
I was trying to hide the keyboard when the back button is pressed but its actually going back to previous menu when the back button is pressed. What I have is a simple activity with SupportSearchFragment.
public class SearchActivity extends LeanbackActivity {
private SearchFragment mFragment;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search);
mFragment = (SearchFragment) getSupportFragmentManager()
.findFragmentById(R.id.search_fragment);
}
#Override
public boolean onSearchRequested() {
mFragment.startRecognition();
return true;
}
}
The fragment is
public class SearchFragment extends SearchSupportFragment
implements SearchSupportFragment.SearchResultProvider {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setSearchResultProvider(this);
}
#Override
public ObjectAdapter getResultsAdapter() {
return null;
}
#Override
public boolean onQueryTextChange(String newQuery) {
loadQuery(newQuery);
return true;
}
#Override
public boolean onQueryTextSubmit(String query) {
loadQuery(query);
return true;
}
}
What I am looking for is, once the back button is pressed, it should hide the keyboard and then another press of back button, it should go to the previous menu.
What I have found is if the ArrayObjectAdapter has any items to show, then back button directly takes to the previous menu. However, if there is no item, it hides the keyboard.
Any suggestion? Thanks in Advance.
First override onBackPressed ,ref
override fun onBackPressed(){
if( view.keyboardIsVisible() )
this.hideKeyboard()
else
super.onBackPressed(); // optional depending on your needs
}
Implement your KeyIsVisible() , ref
fun View.keyboardIsVisible() = WindowInsetsCompat
.toWindowInsetsCompat(rootWindowInsets)
.isVisible(WindowInsetsCompat.Type.ime())
and Hide Keyboard by
/**
* Use only from Activities, don't use from Fragment (with getActivity) or from Dialog/DialogFragment
*/
fun Activity.hideKeyboard() {
val imm = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
val view = currentFocus ?: View(this)
imm.hideSoftInputFromWindow(view.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
}
Ps , It was too late when I saw your language is java , I have attached refs which will help you converting .
Thanks #Anshul for your answer. I will just what I exactly needed because the back button always hides the keyboard.
in Activity
override fun onBackPressed() {
if (fragment?.isKeyBoardVisible() != true) super.onBackPressed()
}
in fragment
fun isKeyBoardVisible() = ViewCompat.getRootWindowInsets(requireView())?.isVisible(WindowInsetsCompat.Type.ime()) ?: true
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//no inspection SimplifiableIfStatement
if (id == R.id.action_filter) {
FragmentManager fm = getSupportFragmentManager();
if (userType.equals("İş Arayan"))
filterDialogTitle = "İş İlanları Filtre";
else if (userType.equals("Hizmet Arayan"))
filterDialogTitle = "Hizmet İlanları Filtre";
FilterDialogFragment editNameDialogFragment = FilterDialogFragment.newInstance(filterDialogTitle);
editNameDialogFragment.show(fm, "fragment_edit_name");
return true;
}
return super.onOptionsItemSelected(item);
}
I added in Fragment, but it didn' t got called, if i add in MainActivity, it works but i want to call it in Fragment. How can i do this ?
In Fragment you have to call setHasOptionsMenu(true)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
...
}
Then suppose you have to handle menu_item_to_handle_in_fragment item click
For Fragment class
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_to_handle_in_fragment:
// Do onlick on menu action here
return true;
}
return false;
}
For Activity class
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_to_handle_in_fragment:
return false;
}
return false;
}
You need to add setHasOptionMenu(true) in your onCreate of fragment.
When you add this option the fragment lifecycle will calls the onCreateOptionMenu() and onOptionItemSelected().
Follow this steps:
Add setHasOptionsMenu(true) method in onCreate() of your Fragment.
Override onCreateOptionsMenu(Menu menu, MenuInflater inflater) and
onOptionsItemSelected(MenuItem item) methods in your Fragment.
Inside your onOptionsItemSelected(MenuItem item) Activity's method,
make sure you return false when the menu item action would be
implemented in onOptionsItemSelected(MenuItem item) Fragment's
method.
Steps to create Option Menu in fragment
1.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_home, menu);
super.onCreateOptionsMenu(menu, inflater);
}
2.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case android.R.id.home:
//call function as per your requirement
return true;
default:
return false;
}
}
Ok, many answers and none of the above shows how to actually call Frarment.
This is the whole example tested and working.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//activate menu on the top to show icons or menu
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
//this part we need to show back arrow on top so user could use backstack
setupActionBarWithNavController(findNavController(R.id.fragmentContainer))
//setupActionBarWithNavController(findNavController(R.id.fragmentContainer))
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.fragmentContainer)
return navController.navigateUp() || super.onSupportNavigateUp()
}
//show icons in top menu
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
//inflate our menu our menu situated in res menu menu.xml so R.menu.menu(our menu)
menuInflater.inflate(R.menu.menu, menu)
return true
}
//if item in menu selected do ......
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val navController = findNavController(R.id.fragmentContainer)
when (item.itemId) {
**R.id.iconAbout -> navController.navigate(R.id.aboutFragment)**
}
return super.onOptionsItemSelected(item)
}
}
I have a tabbed Actionbar/viewpager layout with three tabs say A, B, and C. In tab C tab(fragment),I am adding another fragment say fragment D. with
DFragment f= new DFragment();
ft.add(android.R.id.content, f, "");
ft.remove(CFragment.this);
ft.addToBackStack(null);
ft.commit();
I modify actionbar in DFragment's onResume to add up button:
ActionBar ab = getActivity().getActionBar();
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowHomeEnabled(true);
Now in DFragment, when I press hardware(phone) Back button, I return to the original Tabbed(ABC) layout with CFragment selected. How can I achieve this functionality with actionbar up button?
Implement OnBackStackChangedListener and add this code to your Fragment Activity.
#Override
public void onCreate(Bundle savedInstanceState) {
//Listen for changes in the back stack
getSupportFragmentManager().addOnBackStackChangedListener(this);
//Handle when activity is recreated like on orientation Change
shouldDisplayHomeUp();
}
#Override
public void onBackStackChanged() {
shouldDisplayHomeUp();
}
public void shouldDisplayHomeUp(){
//Enable Up button only if there are entries in the back stack
boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
getSupportActionBar().setDisplayHomeAsUpEnabled(canGoBack);
}
#Override
public boolean onSupportNavigateUp() {
//This method is called when the up button is pressed. Just the pop back stack.
getSupportFragmentManager().popBackStack();
return true;
}
I got it. just override onOptionsItemSelected in hosting activity and popup the backstack, e.g.
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: {
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
return true;
}
break;
}
}
return super.onOptionsItemSelected(item);
}
Call getActionBar().setDisplayHomeAsUpEnabled(boolean); and getActionBar().setHomeButtonEnabled(boolean); in onBackStackChanged() as explained in an answer below.
If you have one parent activity and want this up button to work as a back button, you can use this code:
add this to the onCreate in your main activity class
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
int stackHeight = getSupportFragmentManager().getBackStackEntryCount();
if (stackHeight > 0) { // if we have something on the stack (doesn't include the current shown fragment)
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} else {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setHomeButtonEnabled(false);
}
}
});
and then add onOptionsItemSelected like so:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getSupportFragmentManager().popBackStack();
return true;
....
}
I generally use this all the time and seems pretty legit
you can go back with up button like back button ;
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
super.onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
I used a combination of Roger Garzon Nieto's and sohailaziz's answers. My app has a single MainActivity, and fragments A, B, C that are loaded into it. My "home" fragment (A) implements OnBackStackChangedListener, and checks the size of the backStack; if it's less than one, then it hides the UP button. Fragments B and C always load the back button (in my design, B is launched from A, and C is launched from B). The MainActivity itself just pops the backstack on UP button tap, and has methods to show/hide the button, which the fragments call:
MainActivity:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
getSupportFragmentManager().popBackStack();
return true;
}
return super.onOptionsItemSelected(item);
}
public void showUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(true); }
public void hideUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(false); }
fragmentA (implements FragmentManager.OnBackStackChangedListener):
public void onCreate(Bundle savedinstanceSate) {
// listen to backstack changes
getActivity().getSupportFragmentManager().addOnBackStackChangedListener(this);
// other fragment init stuff
...
}
public void onBackStackChanged() {
// enable Up button only if there are entries on the backstack
if(getActivity().getSupportFragmentManager().getBackStackEntryCount() < 1) {
((MainActivity)getActivity()).hideUpButton();
}
}
fragmentB, fragmentC:
public void onCreate(Bundle savedinstanceSate) {
// show the UP button
((MainActivity)getActivity()).showUpButton();
// other fragment init stuff
...
}
I know this question is old, but may be someone (like me) also needs it.
If your Activity extends AppCompatActivity, you can use a simpler (two-step) solution:
1 - Whenever you add a non-home fragment just show the up button, right after commiting the fragment transaction. Like this:
// ... add a fragment
// Commit the transaction
transaction.commit();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
2 - Then when UP button is pressed, you hide it.
#Override
public boolean onSupportNavigateUp() {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
return true;
}
That's it.
This worked for me. Override onSupportNavigateUp and onBackPressed, for example (code in Kotlin);
override fun onBackPressed() {
val count = supportFragmentManager.backStackEntryCount
if (count == 0) {
super.onBackPressed()
} else {
supportFragmentManager.popBackStack()
}
}
override fun onSupportNavigateUp(): Boolean {
super.onSupportNavigateUp()
onBackPressed()
return true
}
Now in the fragment, if you display the up arrow
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
Clicking on it takes you back the previous activity.
Kotlin:
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
supportFragmentManager.addOnBackStackChangedListener { setupHomeAsUp() }
setupHomeAsUp()
}
private fun setupHomeAsUp() {
val shouldShow = 0 < supportFragmentManager.backStackEntryCount
supportActionBar?.setDisplayHomeAsUpEnabled(shouldShow)
}
override fun onSupportNavigateUp(): Boolean =
supportFragmentManager.popBackStack().run { true }
...
}
This is a very good and reliable solution: http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/
The guy has made an abstract fragment that handles the backPress behaviour and is switching between the active fragments using the strategy pattern.
For some of you there maybe a little drawback in the abstract class...
Shortly, the solution from the link goes like this:
// Abstract Fragment handling the back presses
public abstract class BackHandledFragment extends Fragment {
protected BackHandlerInterface backHandlerInterface;
public abstract String getTagText();
public abstract boolean onBackPressed();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!(getActivity() instanceof BackHandlerInterface)) {
throw new ClassCastException("Hosting activity must implement BackHandlerInterface");
} else {
backHandlerInterface = (BackHandlerInterface) getActivity();
}
}
#Override
public void onStart() {
super.onStart();
// Mark this fragment as the selected Fragment.
backHandlerInterface.setSelectedFragment(this);
}
public interface BackHandlerInterface {
public void setSelectedFragment(BackHandledFragment backHandledFragment);
}
}
And usage in the activity:
// BASIC ACTIVITY CODE THAT LETS ITS FRAGMENT UTILIZE onBackPress EVENTS
// IN AN ADAPTIVE AND ORGANIZED PATTERN USING BackHandledFragment
public class TheActivity extends FragmentActivity implements BackHandlerInterface {
private BackHandledFragment selectedFragment;
#Override
public void onBackPressed() {
if(selectedFragment == null || !selectedFragment.onBackPressed()) {
// Selected fragment did not consume the back press event.
super.onBackPressed();
}
}
#Override
public void setSelectedFragment(BackHandledFragment selectedFragment) {
this.selectedFragment = selectedFragment;
}
}
If you want to go back to your previous activity if this activity has an empty stack of fragments:
This could be useful if you have a MainActivity and you are navigating to e.g. a SettingsActivity with nested prefernceScreens. NavigateUp will pop fragments until you can finish the SettingsActivity to go back to parentActivity/root.
/**
* On actionbar up-button popping fragments from stack until it is empty.
* #return true if fragment popped or returned to parent activity successfully.
*/
#Override
public boolean onSupportNavigateUp() {
//Pop back stack if the up button is pressed.
boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
if (canGoBack) {
getSupportFragmentManager().popBackStack();
} else {
finish();
return super.onSupportNavigateUp();
}
return true;
}
Note: setDisplayHomeAsUpEnabled(true); in fragment activities onCreate()