I'm using a NavigationDrawer and one Activity where I'm replacing Fragments within the FrameLayout. Navigating through NavigationDrawer and replacing Fragments inside of the Activity is straightforward. I'm not sure how to handle when I replace a Fragment from another Fragment. Example:
I show a Fragment which is a basic List, and when the user clicks on one of the items, I show a new Fragment. Now, from this new Fragment, I may choose to show another Fragment, which results in a deeper Fragment hierarchy. I know that I can navigate through with
.addToBackStack() and in the case of Back button, call
if (getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
from the Activity, but as deeper the Fragment hierarchy gets, the more complicated and dirty it becomes. Is there a good article, or architectural approach to handle a case like this one?
In your scenario you should use Nested Fragments.
You can embed fragments inside fragments.
To nest a fragment, simply call getChildFragmentManager() on the Fragment in which you want to add a fragment. This returns a FragmentManager that you can use like you normally do from the top-level activity to create fragment transactions. For example, here’s some code that adds a fragment from within an existing Fragment class:
Fragment videoFragment = new VideoPlayerFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.video_fragment, videoFragment).commit();
From within a nested fragment, you can get a reference to the parent fragment by calling getParentFragment(). And then your parent backstack and child backstack will be separated.
This is how to do it right. The other question is maybe you should consider using intent and new activity instead of 10 nested fragments? Here is an article about child fragments.link
Stack similar question
Make Fragments tags in your Main Activity:
//----------------Fragment TAGS-----------------------\\
private final String FRAGMENT_BOOK_RIDE = "Frag Book Ride";
private final String FRAGMENT_RIDE_HISTORY = "Frag Ride History";
private final String FRAGMENT_PAYMENT = "Frag Payment";
private final String FRAGMENT_PROFILES_SETTINGS = "Frag Profile & Settings";
private final String FRAGMENT_HELP = "Frag Help";
//-----------------------------------------------------\\
Navigation item selection:
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here. \\
int id = item.getItemId();
switch (id) {
case R.id.nav_book_ride:
removeAllFragments();
getSupportActionBar().setTitle(getResources().getString(R.string.app_name));
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container_fl, new BookRideFragment(getApplicationContext())
, FRAGMENT_BOOK_RIDE)
.commit();
break;
case R.id.nav_ride_history:
removeAllFragments();
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container_fl, new RideHistoryFragment(getApplicationContext())
, FRAGMENT_RIDE_HISTORY)
.commit();
break;
case R.id.nav_payment:
removeAllFragments();
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container_fl, new PaymentFragment(getApplicationContext())
, FRAGMENT_PAYMENT)
.commit();
break;
case R.id.nav_settings:
removeAllFragments();
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container_fl, new ProfileSettingFragment(getApplicationContext())
, FRAGMENT_PROFILES_SETTINGS)
.commit();
break;
case R.id.nav_help:
removeAllFragments();
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container_fl, new HelpFragment(getApplicationContext())
, FRAGMENT_HELP)
.commit();
break;
}
drawer.closeDrawer(GravityCompat.START);
return true;
}
Handling backKey press:
#Override
public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
backKeyMethods();
}
}
/**
* Custom back key method for managing fragment back key behavior
*/
private void backKeyMethods() {
RideHistoryFragment rideHistoryFragment = (RideHistoryFragment) getSupportFragmentManager().
findFragmentByTag(FRAGMENT_RIDE_HISTORY);
PaymentFragment paymentFragment = (PaymentFragment) getSupportFragmentManager().
findFragmentByTag(FRAGMENT_PAYMENT);
HelpFragment helpFragment = (HelpFragment) getSupportFragmentManager().
findFragmentByTag(FRAGMENT_HELP);
ProfileSettingFragment userProfileFragment = (ProfileSettingFragment) getSupportFragmentManager().
findFragmentByTag(FRAGMENT_PROFILES_SETTINGS);
if (rideHistoryFragment != null && rideHistoryFragment.isVisible()) {
navigationView.setCheckedItem(R.id.nav_book_ride);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container_fl, new BookRideFragment(getApplicationContext())
, FRAGMENT_BOOK_RIDE)
.commit();
} else if (paymentFragment != null && paymentFragment.isVisible()) {
navigationView.setCheckedItem(R.id.nav_book_ride);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container_fl, new BookRideFragment(getApplicationContext())
, FRAGMENT_BOOK_RIDE)
.commit();
} else if (userProfileFragment != null && userProfileFragment.isVisible()) {
navigationView.setCheckedItem(R.id.nav_book_ride);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container_fl, new BookRideFragment(getApplicationContext())
, FRAGMENT_BOOK_RIDE)
.commit();
} else if (helpFragment != null && helpFragment.isVisible()) {
navigationView.setCheckedItem(R.id.nav_book_ride);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container_fl, new BookRideFragment(getApplicationContext())
, FRAGMENT_BOOK_RIDE)
.commit();
} else if (getFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
/**
* Remove all fragments in Fragment BackStack
*/
private void removeAllFragments() {
FragmentManager fm = getSupportFragmentManager();
for (int i = 0; i < fm.getBackStackEntryCount(); ++i) {
fm.popBackStack();
}
}
Use addToBackStack(null) whenever you want to go deep, so when you will press backKey it will pop immediate fragment.
Hope it helps.
Related
I am adding a BottomNavigationView with three fragments to my app.
Everything works correctly, except one thing.
In the first fragment, there is an EditText view, in the second a ListView and in the third one some texts and images loaded from a JSON hosted in the server.
This is my code:
bottomNavigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.menu_dash:
fragment = new frg_dash();
break;
case R.id.menu_list:
fragment = new frg_list();
break;
case R.id.menu_info:
fragment = new frg_info();
break;
}
final FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.contenedor_principal, fragment).commit();
return true;
}
});
The problem is that every time I go from one fragment to another using the bottomNavigationView buttons, the Fragment starts all its execution again.
The result I'm looking for is that if the user in the second fragment is, for example, in the middle of the ListView, it goes to the third fragment and returns again, the ListView continues where it was.
Or if you press the button of the third Fragment in the bottomNavigationView, do not load the data again from the server.
I guess the problem is that when you click on a bottomNavigationView button, the fragment is created again:
... switch (id) {
case R.id.menu_dash:
fragment = new frg_dash();
break; ...
But it's just a guess. I suppose it can be controlled with the onCreate, onActivityCreated and onCreateView methods, but again, they are just my assumptions.
I've tried it with the hide () and show () parameters of the fragments, but without success ... or I'm not applying it well
I greatly appreciate the help in advance.
EDIT
This is my example currently with all the parts related to the answer:
public void replaceFragment(Fragment fragment, #Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
Log.v("2134", "Dentro");
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
String tag = fragment.getClass().getName();
Log.v("2134", "tag:" + tag);
Fragment parentFragment;
if (findInStack && fm.findFragmentByTag(tag) != null) {
parentFragment = fm.findFragmentByTag(tag);
} else {
parentFragment = fragment;
}
// if user passes the #bundle in not null, then can be added to the fragment
if (bundle != null) {
parentFragment.setArguments(bundle);
} else {
parentFragment.setArguments(null);
}
// this is for the very first fragment not to be added into the back stack.
if (popBackStack) {
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
} else {
ft.addToBackStack(parentFragment.getClass().getName() + "");
}
ft.replace(R.id.contenedor_principal, parentFragment, tag);
ft.commit();
fm.executePendingTransactions();
}
...
bottomNavigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.menu_panel:
fragment = new frg_panel();
break;
case R.id.menu_promos:
fragment = new frg_promociones();
break;
case R.id.menu_catalogo:
fragment = new frg_catalogo();
break;
}
replaceFragment(fragment, null, true, true);
return true;
}
});
Use this code to open your fragment. Your fragment will not create every time. It will get same fragment from stack if exist.
/**
* replace or add fragment to the container
*
* #param fragment pass android.support.v4.app.Fragment
* #param bundle pass your extra bundle if any
* #param popBackStack if true it will clear back stack
* #param findInStack if true it will load old fragment if found
*/
public void replaceFragment(Fragment fragment, #Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
String tag = fragment.getClass().getName();
Fragment parentFragment;
if (findInStack && fm.findFragmentByTag(tag) != null) {
parentFragment = fm.findFragmentByTag(tag);
} else {
parentFragment = fragment;
}
// if user passes the #bundle in not null, then can be added to the fragment
if (bundle != null)
parentFragment.setArguments(bundle);
else parentFragment.setArguments(null);
// this is for the very first fragment not to be added into the back stack.
if (popBackStack) {
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
} else {
ft.addToBackStack(parentFragment.getClass().getName() + "");
}
ft.replace(R.id.contenedor_principal, parentFragment, tag);
ft.commit();
fm.executePendingTransactions();
}
use it like
Update :
If your fragment is home or dashboard fragment then
Fragment f = new YourFragment();
replaceFragment(f, null, true, true);
Otherwise
Fragment f = new YourFragment();
replaceFragment(f, null, false, true);
Important This code is not replacement of saving all states or variables in fragment. This code will useful because it will not create fragment instance again.
For saving all states and variables in fragment for future use see this answer
okay i know there are other questions that on first glance make this one look like a duplicate, but none of these answers work in my case,
What i want is the first fragment displayed to be like a Main Activity in respect to how the back button works, i need whichever fragment i choose from my navigation drawer to go back to the first fragment when the back button is pressed then a user would quit the app by pressing it again.
So ive tried using addToBackStack and when i move to another fragment if i press the back button it comes back to my first fragment (exactly as i want) but pressing the back button again leaves me with a white screen (i wonder if this is due to the transaction animation im using which ive included below) so to get around this i tried overriding the back button and throwing in a call to finish(); but this causes whichever fragment im in to finish instead of going back to the first fragment, ive tried a handful of workarounds from the above mentioned link and many others but cannot find a decent fix any suggestions?
here is my Main Activity displayView
private void displayView(int position) {
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
fragment = new FirstFragment();
break;
case 1:
fragment = new glideFrag();
break;
case 2:
fragment = new secondGlideFrag();
break;
case 3:
fragment = new thirdGlideFrag();
break;
case 4:
fragment = new forthGlideFrag();
break;
case 5:
fragment = new lgFrag();
break;
case 6:
fragment = new cyanFrag();
break;
case 7:
fragment = new sonyFrag();
break;
case 8:
fragment = new SecondFragment();
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.enter,R.anim.exit,R.anim.pop_enter,R.anim.pop_exit);
//fragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.frame_container, fragment).addToBackStack("first Fragment").commit();
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
mDrawerList.setSelection(position);
setTitle(navMenuTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
} else {
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
i found this that looks like a great way around it
private boolean popNext = false;
if(popNext){
if(position == INITIAL_POSITION){
onBackPressed();
mDrawerLayout.closeDrawer(mDrawerList);
popNext = false;
return;
}
getSupportFragmentManager().popBackStackImmediate();
}
else{
if(position == INITIAL_POSITION){
mDrawerLayout.closeDrawer(mDrawerList);
return;
}
popNext=true;
}
but im still fairly new to android and im not sure what to set INITIAL_POSITION to, I tried
private static final INITIAL_POSITION = 0;
but without any luck
When adding the initial fragment, you must not add it to the back stack.
You must only do it for the next ones. When the back stack will be empty, the Activity will just finish.
Edit: Here is an explanation of the problem so you can figure out how to fix it:
Each time you add a fragment transaction to the back stack, you allow the user to revert it by pressing the back button and the Activity will return to the state it was before the transaction. If the initial fragment is added to the back stack, then when the user press back, the screen becomes blank, because there was nothing displayed before you added the initial fragment. That's why the initial fragment transaction which adds the first visible fragment to your Activity must not be added to the back stack. Usually you initialize the initial fragment like this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState == null) {
Fragment fragment = new FirstFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.frame_container, fragment)
.commit();
}
}
BladeCoders answer was more trying to tell me how the backstack works rather than answering my question, i ended up not adding any fragments to the back stack, .addToBackStack(null), and overriding back button in MainActivity, feels like a little bit of a hack but works perfectly
#Override
public void onBackPressed() {
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() < 1){
Fragment fragment = new FirstFragment();
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.enter, R.anim.exit,
R.anim.pop_enter, R.anim.pop_exit);
getSupportActionBar().setTitle(mDrawerTitle);
fragmentTransaction.replace(R.id.frame_container,
fragment).addToBackStack("first").commit();
}else{
finish();
}
}
You can do it even with out backstack its just my point of view to simplify so that it can help some one.
#Override
public void onBackPressed(){
Fragment f = getSupportFragmentManager().findFragmentById(R.id.container_body);
if(f.getClass().getName().equals(HomeFragment.class.getName())){ // here HomeFragment.class.getName() means from which faragment you actually want to exit
finish();
}else{
displayView(0); //were you want to go when back button is pressed
}
}
private void displayView(int position) {
Fragment fragment = null;
String title = getString(R.string.app_name);
switch (position) {
case 0:
fragment = new HomeFragment();
title = getString(R.string.app_name);
break;
case 1:
fragment = new OffersFragment();
title = getString(R.string.nav_item_offers);
break;
case 2:
fragment = new NotificationFragment();
title = getString(R.string.nav_item_notifications);
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.container_body, fragment);
fragmentTransaction.commit();
// set the toolbar title
getSupportActionBar().setTitle(title);
}
}
My activity is composed of 3 nested Fragments. There is my MainFragment that is displayed by default, ProductFragment that can be called from it, then DetailFragment can be called from ProductFragment.
I can go back and forth between my ProductFragment and DetailFragment. By doing so, the popStackBack method is accumulating similar fragments. Then, if I click on the back button, It will go back through all the Fragments as many time I called them.
What is the proper way to avoid the same Fragment to be kept in the back stack ?
EDIT :
I firstly call my main fragment :
if (savedInstanceState == null) {
getFragmentManager()
.beginTransaction()
.add(R.id.container, new SearchFragment(), "SEARCH_TAG")
.commit();
}
Here is the code that calls the fragments from the activity :
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.enter_from_bottom, R.animator.exit_to_top, R.animator.enter_from_bottom, R.animator.exit_to_top);
ft.replace(R.id.container, new FactFragment(), "FACT_TAG");
ft.addToBackStack("FACT_TAG");
ft.commit();
Then, on back click :
#Override
public void onBackPressed() {
getFragmentManager().popBackStack();
}
I tried to get the tag of my current fragment and execute some specific code related to it but it doesn't work well. I also tried to addToBackStack() only when current Fragment wasn't already added to the backStack but it messed up my fragment view.
Use fragment's method isAdded() to evaluate the insertion. For example:
if(!frag.isAdded()){
//do fragment transaction and add frag
}
Here is my solution. Maybe dirty but it works. I implemented a method that returns the tag of the fragment that is displayed before clicking the on back button :
public String getActiveFragment() {
if (getFragmentManager().getBackStackEntryCount() == 0) {
return null;
}
String tag = getFragmentManager()
.getBackStackEntryAt(getFragmentManager()
.getBackStackEntryCount() - 1)
.getName();
return tag;
}
Then, on my onBackPressed() method :
// Get current Fragment tag
String currentFrag = getActiveFragment();
if(currentFrag.equals("PRODUCT_TAG")) {
// New transaction to first Fragment
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.enter_from_right, R.animator.exit_to_left, R.animator.enter_from_right, R.animator.exit_to_left);
ft.replace(R.id.container, new SearchFragment(), "MAIN_TAG");
ft.commit();
} else {
// Go to Fragment-1
getFragmentManager().popBackStack();
}
Here is my handy and simple solution to check for duplicate insertion through fragment manager
at first, I check if it is first time intention for adding fragment and then I check if the fragment is presented using fragment manager
Fragment fragment = getSupportFragmentManager().findFragmentByTag("firstFragment");
if (fragment == null) {
getSupportFragmentManager().beginTransaction().add(R.id.frameLayout, new FirstFragment(), "firstFragment")
.addToBackStack(null)
.commit();
}else if(!fragment.isAdded()){
getSupportFragmentManager().beginTransaction().add(R.id.frameLayout, new FirstFragment(), "firstFragment")
.addToBackStack(null)
.commit();
}
Here is my solution:
Fragment curFragment = fragmentManager.findFragmentById(R.id.frameLayout);
if(curFragment != null
&& curFragment.getClass().equals(fragment.getClass())) return;
// add the fragment to BackStack here
Xamarin.Android (C#) version:
var curFragment = fragmentManager.FindFragmentById(Resource.Id.frameLayout);
if (curFragment != null
&& curFragment.GetType().Name == fragment.GetType().Name) return;
// add the fragment to BackStack here
None of the other questions I have read on stackoverflow have been able to help with my problem. As far as I can tell, I am doing everything correctly.
I have a master/detail flow with fragments.
Upon creation of the main activity, the master fragment is loaded with the following code:
Fragment frag;
frag = new MainListFragment();//<-- **the master fragment**
FragmentManager fm = getFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction.replace(R.id.fragment_container, frag);
Log.d("My Debug Bitches", "stack:" + fm.getBackStackEntryCount());
transaction.commit();
The master fragment has a ListView; clicking on a list item brings up the details fragment like so:
#Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
FragmentManager fm = getFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
SubListFragment frag = new SubListFragment();//<-- **the detail fragment**
transaction.replace(R.id.fragment_container, frag);
transaction.addToBackStack(null);
transaction.commit();
fm.executePendingTransactions();
Log.d("My Debug Bitches", "stack:" + fm.getBackStackEntryCount());
}
Now, according to LogCat, the BackStackEntryCount changes from 0 to 1 after I navigate from master fragment to detail fragment:
So why is it that, when I click the back button while in the details fragment, that the app closes instead of returning to the master fragment??????????
You have to add the popBackStack() call to the onBackPressed() method of the activity.
Ex:
#Override
public void onBackPressed() {
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStack();
} else {
super.onBackPressed();
}
}
#Bobbake4's answer is awesome, but there is one little problem.
Let's say I have three fragments A, B and C.
A is the main Fragment (the fragment that shows when I launch my app), B and C are fragments I can navigate to from the navigation drawer or from A.
Now, when I use the back button from B or C, I go back to the previous fragment (A) alright, but the title of the previous fragment (fragment B or C) now shows in the actionBar title of Fragment A. I have to press the back button again to "truly" complete the back navigation (to display the view and correct title for the fragment and returning to)
This is how I solved this problem. Declare these variables.
public static boolean IS_FRAG_A_SHOWN = false;
public static boolean IS_FRAG_B_SHOWN = false;
public static boolean IS_FRAG_C_SHOWN = false;
In the MainActivity of my app where am handling navigation drawer methods, I have a method displayView(position) which handles switching of my fragments.
private void displayView(int position) {
IS_FRAG_A_SHOWN = false;
IS_FRAG_B_SHOWN = false;
IS_FRAG_C_SHOWN = false;
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
fragment = new FragmentA();
IS_FRAG_A_SHOWN = true;
break;
case 1:
fragment = new FragmentB();
IS_FRAG_B_SHOWN = true;
break;
case 2:
fragment = new FragmentC();
IS_FRAG_C_SHOWN = true;
break;
default:
break;
}
finally, in my onBackPressed method, I do this:
public void onBackPressed() {
if(fragmentManager.getBackStackEntryCount() != 0) {
fragmentManager.popBackStack();
if (IS_FRAG_A_SHOWN) { //If we are in fragment A when we press the back button, finish is called to exit
finish();
} else {
displayView(0); //else, switch to fragment A
}
} else {
super.onBackPressed();
}
}
I'm using the built-in navigation drawer to run my app. I can't quite figure out how to handle the back button. When it's pressed I want it to load the very first fragment again. Fragment1.
So when the app launches you see Fragment1 launched. They can then click on Fragment 2-5 to go to other pages. Within all of these pages, I want the back button to take the user back to Fragment1. The only place the user should be able to exit the app via the back button is Fragment1.
Since it's all handled by a FragmentActivity I tried messing with the back button there. I keep getting a force close error, however:
(01-11 14:09:33.114: E/AndroidRuntime(8292): android.view.InflateException: Binary XML file line #7: Error inflating class fragment)
This is what I have so far:
I've made sure to add the fragments to the back stack like this:
fm.beginTransaction().replace(R.id.main, newFragment).addToBackStack("fragBack").commit();
Back button:
#Override
public void onBackPressed() {
if (getSupportFragmentManager().findFragmentByTag("fragBack") != null) {
}
else {
super.onBackPressed();
return;
}
if (getSupportFragmentManager().getBackStackEntryCount() != 0) {
Toast.makeText(getApplicationContext(), "Test", Toast.LENGTH_LONG).show();
Fragment frag = getSupportFragmentManager().findFragmentByTag("fragBack");
FragmentTransaction transac = getSupportFragmentManager().beginTransaction().remove(frag);
transac.commit();
}
}
Does anyone know what I need to do? Do I need to call onBackPressed in every fragment (if that's even possible) rather than the FragmentActivity that controls the drawer? In my past apps I've been OK with the back button closing the app regardless of which Fragment the user is on but the one I'm making now I want the back button to go back to Fragment1.
Would really appreciate some help, thank you.
onItemClick
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Fragment newFragment = new MapsPage();
FragmentManager fm = getSupportFragmentManager();
switch(i) {
case 0:
newFragment = new Fragment2();
break;
case 1:
newFragment = new Fragment3();
break;
case 2:
newFragment = new Fragment4();
break;
case 3:
newFragment = new Fragment5();
break;
}
fm.beginTransaction().add(R.id.main, newFragment).addToBackStack("fragback").commit();
drawerLayout.closeDrawer(rl);
}
Instead of:
fm.beginTransaction().replace(R.id.main, newFragment).addToBackStack("fragBack").commit();
Call:
fm.beginTransaction().add(R.id.main, newFragment).addToBackStack("fragBack").commit();
addToBackStack works with add.
replace function removes previous fragment and places new fragment so on your back-stack there is only one fragment all the time. So use add function to keep previous fragments on stack.
To always goto fragemnt1 from any fragment onBackPress try to do following:
getFragmentManager().popBackStack();
fm.beginTransaction().add(R.id.main, newFragment).addToBackStack("fragBack").commit();
this will remove last transaction from backstack and add new one.
Try this.
Just wanted to report my findings even though this question is a little old for anyone else who may have had the same problem with the accepted answer. For me, doing the method suggested in the accepted answer, made the layers overlap, quickly making them unreadable. The code below (adapted from the accepted answer) avoids the overlaying but still adds the screen to the back stack.
fragmentManager.beginTransaction().replace(R.id.container, fragment).addToBackStack("fragBack").commit();
In some cases you have to use replace then you cant work with addtobackstack() so you can use this code in MainActivity. In this code when you press back key you always go to first fragment (i call it HomeFragment) and when you are in HomeFragment it ask twice time to go out from application.
private Boolean exit = false;
#Override
public void onBackPressed() {
if (exit) {
super.onBackPressed();
return;
}
try {
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag("HOME");
if (fragment != null) {
if (fragment.isVisible()) {
this.exit = true;
Toast.makeText(this, "Press Back again to Exit", Toast.LENGTH_SHORT).show();
}
}
else {
fragment = HomeFragment.class.newInstance();
getFragmentManager().popBackStack();
fragmentManager.beginTransaction().replace(R.id.flContent, fragment, "HOME").commit();
}
} catch (Exception e) {
}
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
exit = false;
}
}, 2000);
}
- #Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
int backstack = getSupportFragmentManager().getBackStackEntryCount();
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else if (backstack > 0) {
for (int i = 0; i < backstack; i++) {
getSupportFragmentManager().popBackStackImmediate();
}
} else {
this.finish();
}
}
I have 4 fragment attached to bottomnavigation activity
and i recently got into this problem and successfulyy solved as follows
my activity code
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.NavigationUI;
import android.os.Bundle;
import android.view.MenuItem;
import com.google.android.material.bottomnavigation.BottomNavigationView;
public class BottomNavigationActivity extends AppCompatActivity
{
NavController navController;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bottom_navigation);
BottomNavigationView bottomNavigationView=findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(navListener);
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new FragmentHome()).commit();
}
public boolean onSupportNavigateUp() {
return navController.navigateUp();
}
private BottomNavigationView.OnNavigationItemSelectedListener navListener= new
BottomNavigationView.OnNavigationItemSelectedListener()
{
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item)
{
Fragment selectedFragment = null ;
switch (item.getItemId())
{
case R.id.nav_home:
selectedFragment = new FragmentHome();
break;
case R.id.nav_search:
BottomNavigationActivity.this.getSupportFragmentManager().beginTransaction()
.addToBackStack(null)
.commit();
selectedFragment=new FragmentSearch();
break;
case R.id.nav_cart:
BottomNavigationActivity.this.getSupportFragmentManager().beginTransaction()
.addToBackStack(null)
.commit();
selectedFragment=new FragmentCart();
break;
case R.id.nav_user:
BottomNavigationActivity.this.getSupportFragmentManager().beginTransaction()
.addToBackStack(null)
.commit();
selectedFragment= new FragmentAccount();
break;
}
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
selectedFragment).commit();
return true;
}
};
#Override
public void onBackPressed()
{
int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
if (backStackEntryCount == 0) {
super.onBackPressed();
} else
{
goHome();
}
}
public void goHome()
{
//Following code will set the icon of the bottom navigation to active
final BottomNavigationView mBottomNav = findViewById(R.id.bottom_navigation);
MenuItem homeItem = mBottomNav.getMenu().getItem(0);
mBottomNav.setSelectedItemId(homeItem.getItemId());
getSupportFragmentManager().popBackStackImmediate();
//To delete all entries from back stack immediately one by one.
int backStackEntry = getSupportFragmentManager().getBackStackEntryCount();
for (int i = 0; i < backStackEntry; i++) {
getSupportFragmentManager().popBackStackImmediate();
}
//To navigate to the Home Fragment
final FragmentHome homeFragment = new FragmentHome();
FragmentTransaction myFragmentTransaction = getSupportFragmentManager().beginTransaction();
myFragmentTransaction.replace(R.id.fragment_container, homeFragment, "HomeFrag Tag");
myFragmentTransaction.commit();
}
}
I would suggest avoiding an override of onBackPressed() altogether by managing your transactions properly in the first place. This will help to avoid having to implement crazy logic down the road.
To do this first we need to set a private class variable in that will enable initialization:
private boolean popNext = false;
The following code allows us to setup the initial back function by placing it on the stack. Every time thereafter, when popNext is set to true, we pop the initial transaction and push the new one. So we are replacing the 1>X transaction with the 1>Y transaction.
The extra embedded if statements deal with selecting the initial item, since we don't want to go from 1>1. If it's our initial case we just close the drawer. The other case needs to act like the back button, but we need to remember to set it as if it is returning to the initial state!
if(popNext){
if(i == INITIAL_POSITION){
onBackPressed();
mDrawerLayout.closeDrawer(mDrawerList);
popNext = false;
return;
}
getFragmentManager().popBackStackImmediate();
}
else{
if(i == INITIAL_POSITION){
mDrawerLayout.closeDrawer(mDrawerList);
return;
}
popNext=true;
}
getFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit();
Note: My code uses getFragmentManager() instead of getSupportFragmentManager() which is a native function, I believe as of Honeycomb.