Popbackstack is working fine when all the fragments in the sequence are added in the backstack but isnt working when one of the transactions is not added in the backstack.
Here is my navigation:
1.Replace fragment to load home fragment. This transaction not added to backstack.
Replace fragment to load login fragment. This transaction is added to backstack.
3.Replace fragment to load loggedin fragment. This transaction is not added to backstack.
Now, when i press back button once nothing happens. Whereas ideally it should go to the home fragment from logged in fragment.
Here is my onbackpressed method in main activity:
#Override
public void onBackPressed() {
if(getSupportFragmentManager().getBackStackEntryCount()>0)
{
FragmentManager.BackStackEntry backStackEntry = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1);
String str = backStackEntry.getName();
FragmentManager fm=getSupportFragmentManager();
//getSupportFragmentManager().popBackStackImmediate();
fm.popBackStack(str, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
else {
super.onBackPressed();
}
}
popBackstack only 'pop' what is in the backstack.
Since you haven't add the transaction when replacing the LoginFragment by the LoggedInFragment when you press back:
the LoggedInFragment remains,
the LogInFragment is popped
the HomeFragment is displayed
But because the LoggedInFragment as been added after the HomeFragment, the HomeFragment is displayed underneath it. So you can't see it as hidden by the LoggedInFragment.
One solution is to add the transaction to the back stack when you replace the LogInFragment by the LoggedInFragment.
Then in onBackPressed you test if the current fragment is the LoggedInFragment. If it's the case you pop the back stack up to HomeFragment (not inclusive). Like that both LoggedInFragment and LogInFragment will be pop.
EDIT
#Override
public void onBackPressed() {
FragmentManager manager = getSupportFragmentManager();
Fragment fragment = manager.findFragmentById(R.id.my_fragment_container);
// If there is something in the back stack AND the current fragment is the LoggedInFragment
if (manager.getBackStackEntryCount() > 0
&& fragment instanceof LoggedInFragment) {
manager.popBackStack(HomeFragment.class.getSimpleName(), 0);
} else {
super.onBackPressed();
}
}
In order to retrieve the HomeFragment by name you need to tag your transaction when you replace the current fragment by the HomeFragment. Generally I tag all transactions with the fragment's class simple name so like that I can retried any fragment:
transaction.replace(R.id.my_fragment_container, fragment, fragment.getClass().getSimpleName());
Eselfar's explanation of the problem is correct, but the code he provided wasn't generic enough for me.
I (hopefully) resolved this issue in a general case by the following code:
#Override
public void onBackPressed() {
Fragment currentFragment = getCurrentFragment();
if (mFragmentManager.getBackStackEntryCount() > 0) {
// In a normal world, just popping back stack would be sufficient, but since android
// is not normal, a call to popBackStack can leave the popped fragment on screen.
// Therefore, we start with manual removal of the current fragment.
removeCurrentFragment();
mFragmentManager.popBackStack();
} else {
super.onBackPressed();
}
}
private Fragment getCurrentFragment() {
return mFragmentManager.findFragmentById(getContentFrameId());
}
private void removeCurrentFragment() {
FragmentTransaction ft = mFragmentManager.beginTransaction();
ft.remove(getCurrentFragment());
ft.commit();
// not sure it is needed; will keep it as a reminder to myself if there will be problems
// mFragmentManager.executePendingTransactions();
}
I've created a fragment that shows gridview and when any griditem is clicked it leds to another fragment. But when I press the physical backbutton the app closes instead of going back to previous fragment (i.e. fragment containing gridview). How can I solve this?
try this one
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0 ){
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
'addToBackStack' is used for moving back to previous fragment, you can use a common Function
in your Main activity for changing fragment.
public void change_fragment(Fragment fragment, int frame) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction trans = manager.beginTransaction();
//trans.setCustomAnimations(R.anim.enterfrom_left, R.anim.exit_to_right,R.anim.enterfrom_left, R.anim.exit_to_right);
trans.replace(frame, fragment);
trans.addToBackStack("hai" + frame);
trans.commit();
}
you can call it from Main activity like this
change_fragment(new Frag(),R.id.fl_main_frag_container);
you can call it from another fragment like this
((MainActivity)getContext()).change_fragment(new Frag(), R.id.fl_main_frag_container);
I'm wondering which is the proper way to change Fragments, add them to backstack, and restore the visibile Fragment after a screen rotation.
Currently, I use this method to initialize the first Fragment:
private void inflateInitialFragment() {
FragmentManager manager = getFragmentManager();
Fragment mainFragment = manager.findFragmentByTag(MainMenuFragment.class.getSimpleName());
FragmentTransaction ft = manager.beginTransaction();
if (mainFragment == null) {
ft.replace(R.id.mainContainer, new MainMenuFragment(), MainMenuFragment.class.getSimpleName());
} else if (!(mainFragment.isAdded() && !mainFragment.isDetached() && !mainFragment.isRemoving())) {
ft.replace(R.id.mainContainer, mainFragment, MainMenuFragment.class.getSimpleName());
}
ft.commit();
manager.executePendingTransactions();
}
And then to display new Fragments I have methods like this one:
public void openAwards() {
getFragmentManager().beginTransaction().replace(R.id.mainContainer,
new AwardsFragment(), AwardsFragment.class.getSimpleName()).addToBackStack(null).commit();
}
And to go back to the main screen:
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
} else {
getFragmentManager().popBackStack();
}
}
After a few screen rotations, I've got crashes like this one:
java.lang.IllegalStateException: Fragment already added: MainMenuFragment{42c64d90 #0 id=0x7f0b003f MainMenuFragment}
How should I change the visible Fragments and restore them after a screen rotation?
I don't think that saving some string or Fragment each time is a good solution to restore them.
If your Activity extends android.app.Activity you don't need to override onBackPressed(). It will pop your fragments from back stack automatically.
I have an application with navigation drawer. when i start the application, what i have on main screen (screen A) is "hello world" and then when i select an item from navigation drawer, i load a fragment and get "new hello world" and then again when i select an item from navigation drawer, i load a fragment and get "hello universe". But since this is all happening via 1 single fragment, when i press the device back button i should get previous fragment like below:
"hello universe" >press back> "new hello world" >press back> "hello world"
how do i handle this ?
NOTE:
while changing the fragment i have tried
fragmentManager.beginTransaction().replace(R.id.mainContent, fragment).commit();
then i changed to :
fragmentManager.beginTransaction().add(R.id.mainContent, fragment).addToBackStack("tag").commit();
but nothing worked. The app exits on back button press.
Is it due to the same fragment getting replaced by another content again and again?
The second approach you tried is correct.Try to call popBackStack() on BackPressed() by overriding onBackPressed() method.
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStack();
} else if (getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
}
}
I think you need to add the fragment to the backstack by calling addToBackStack, something like this:
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
if (fragmentManager.findFragmentByTag("MainFragment") == null) {
fragmentTransaction.add(R.id.content_frame, fragment, "MainFragment");
} else {
fragmentTransaction.replace(R.id.content_frame, fragment, "MainFragment");
fragmentTransaction.addToBackStack("Fragment" + position);
}
fragmentTransaction.commit();
and the override OnBackPressed():
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0 ){
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
use methods addToBackStack and popBackStack in order to handle back button for fragments here is sample
ft.replace(R.id.simple_fragment, newFragment);
ft.addToBackStack("tag");
ft.commit();
and on back button
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
}
Say I have an activity that has fragments added programmatically:
private void animateToFragment(Fragment newFragment, String tag) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.fragment_container, newFragment, tag);
ft.addToBackStack(null);
ft.commit();
}
What is the best way to return to the previous fragment that was visible?
I found Trigger back-button functionality on button click in Android but I'm thinking simulating a back key event isn't the right way to go about it (and I can't get it to work either):
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
Calling finish() just closes the activity which I'm not interested in.
Is there a better way to go about this?
Look at the getFragmentManager().popBackStack() methods (there are several to choose from)
http://developer.android.com/reference/android/app/FragmentManager.html#popBackStack()
To elaborate on the other answers provided, this is my solution (placed in an Activity):
#Override
public void onBackPressed(){
FragmentManager fm = getFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
Log.i("MainActivity", "popping backstack");
fm.popBackStack();
} else {
Log.i("MainActivity", "nothing on backstack, calling super");
super.onBackPressed();
}
}
When we are updating/add the fragments,
Should Include the .addToBackStack().
getSupportFragmentManager().beginTransaction()
.add(detailFragment, "detail") // Add this transaction to the back stack (name is an optional name for this back stack state, or null).
.addToBackStack(null)
.commit();
After that if we give the getFragments.popBackStackImmediate() will return true if we add/update the fragments, and move back to the current screen.
Android Navigation architecture component.
The following code works for me:
findNavController().popBackStack()
These answers does not work if i don't have addToBackStack() added to my fragment transaction but, you can use:
getActivity().onBackPressed();
from your any fragment to go back one step;
Add those line to your onBackPressed() Method. popBackStackImmediate() method will get you back to the previous fragment if you have any fragment on back stack
`
if(getFragmentManager().getBackStackEntryCount() > 0){
getFragmentManager().popBackStackImmediate();
}
else{
super.onBackPressed();
}
`
This solution works perfectly for bottom bar based fragment navigation when you want to close the app when back pressed in primary fragment.
On the other hand when you are opening the secondary fragment (fragment in fragment) which is defined as "DetailedPizza" in my code it will return the previous state of primary fragment. Cheers !
Inside activities on back pressed put this:
Fragment home = getSupportFragmentManager().findFragmentByTag("DetailedPizza");
if (home instanceof FragmentDetailedPizza && home.isVisible()) {
if (getFragmentManager().getBackStackEntryCount() != 0) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
} else {
//Primary fragment
moveTaskToBack(true);
}
And launch the other fragment like this:
Fragment someFragment = new FragmentDetailedPizza();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.container_body, someFragment, "DetailedPizza");
transaction.addToBackStack("DetailedPizza");
transaction.commit();
Kotlin Answer
First, call Fragment Manager.
After, to use onBackPressed()
method.
Coding in Android Studio 4.0 with Kotlin:
fragmentManager?.popBackStack()
Programmatically go back to the previous fragment using following code.
if ( getFragmentManager().getBackStackEntryCount() > 0)
{
getFragmentManager().popBackStack();
return;
}
super.onBackPressed();
To make that fragment come again, just add that fragment to backstack which you want to come on back pressed, Eg:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Fragment fragment = new LoginFragment();
//replacing the fragment
if (fragment != null) {
FragmentTransaction ft = ((FragmentActivity)getContext()).getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.addToBackStack("SignupFragment");
ft.commit();
}
}
});
In the above case, I am opening LoginFragment when Button button is pressed, right now the user is in SignupFragment. So if addToBackStack(TAG) is called, where TAG = "SignupFragment", then when back button is pressed in LoginFragment, we come back to SignUpFragment.
Happy Coding!
By adding fragment_tran.addToBackStack(null) on last fragment, I am able to do come back on last fragment.
adding new fragment:
view.findViewById(R.id.changepass).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.container, new ChangePassword());
transaction.addToBackStack(null);
transaction.commit();
}
});
Following Kotlin code useful to me
1. Added in Simple Activity class with multiple fragments used
override fun onBackPressed() {
if (supportFragmentManager.backStackEntryCount > 0) {
Log.i(TAG, "=============onBackPressed - Popping backstack====")
supportFragmentManager.popBackStack()
} else {
Log.i(TAG, "=============onBackPressed called because nothing on backstack====")
super.onBackPressed()
}
}
2. Added in BottomNavigationView Activity class with multiple fragments used
override fun onBackPressed() {
Log.e(TAG, "=============onBackPressed")
val navController = findNavController(R.id.nav_host_fragment)
when (navController.currentDestination!!.id) {
R.id.navigation_comments, R.id.navigation_my_posts -> {
menuItemPosition = 0
navController.navigate(R.id.navigation_home)
Log.i(TAG, "=============onBackPressed - Popping backstack with First fragment ====")
}
else -> {
Log.i(TAG, "=============onBackPressed called because nothing on backstack====")
super.onBackPressed()
}
}
}
I came here looking or the same idea, and in the meantime i came up with own, which I believe is not that bad and works if with ViewPager.
So what I did, is to override the onBackPressed method in the parent activity that holds the viewPager, and set it to always go back minus 1 position until it reaches the first fragment, then closes the activity.
#Override
public void onBackPressed() {
if(viewPager.getCurrentItem()>0){
viewPager.setCurrentItem(viewPager.getCurrentItem()-1);
} else {
super.onBackPressed();
this.close();
}
}
private void close(){
this.finish();
}
This might have a downfalls, like it only goes back one way left each time, so it might not work great if there are tabs and you switch positions with fragments skipped, ( going from 0 to 2, and then pressing back would put you on 1, instead of 0)
For my case tho, with 2 fragments in viewPager without tabs, it does the job nicely.
getActivity().getSupportFragmentManager().popBackStackImmediate();
OR
getActivity().onBackPressed();
Try below code:
#Override
public void onBackPressed() {
Fragment myFragment = getSupportFragmentManager().findFragmentById(R.id.container);
if (myFragment != null && myFragment instanceof StepOneFragment) {
finish();
} else {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
}