Removing a Fragment from the back stack - android

I have a 3 fragments in an activity when the a tablet is held in portrait. However I only have 2 of these fragments when in landscape. The problem I am having is when going from portrait to landscape the activity is creating the 3rd fragment. I receive and error as this fragment cannot be created.
I have worked out that this fragment is being created because it is in the back stack.
I have tried to remove the fragment in the onDestroy method by using
FragmentTransaction f = fragmentManager.beginTransaction();
f.remove(mf);
f.commit();
However the I get an error saying that I cannot use this function after the onSaveInstanceState
What would be the correct way of taking this fragment out of the back stack?
Update
I should probably add that the fragment I am having problems with is a mapFragment from this libary
https://github.com/petedoyle/android-support-v4-googlemaps
The way I use it is like so
mf = MapFragment.newInstance(1, true);
ft = fragmentManager.beginTransaction();
ft.replace(R.id.mapContainer, mf);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack("map");
ft.commit();

You add to the back state from the FragmentTransaction and remove from the backstack using FragmentManager pop methods:
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction trans = manager.beginTransaction();
trans.remove(myFrag);
trans.commit();
manager.popBackStack();

I created a code to jump to the desired back stack index, it worked fine to my purpose.
ie. I have Fragment1, Fragment2 and Fragment3, I want to jump from Fragment3 to Fragment1
I created a method called onBackPressed in Fragment3 that jumps to Fragment1
Fragment3:
public void onBackPressed() {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.popBackStack(fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount()-2).getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
In the activity, I need to know if my current fragment is the Fragment3, so I call the onBackPressed of my fragment instead calling super
FragmentActivity:
#Override
public void onBackPressed() {
Fragment f = getSupportFragmentManager().findFragmentById(R.id.my_fragment_container);
if (f instanceof Fragment3)
{
((Fragment3)f).onBackPressed();
} else {
super.onBackPressed();
}
}

you show fragment in a container (with id= fragmentcontainer) so you remove fragment with:
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragmentContainer);
fragmentTransaction.remove(fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();

In case you ended up here trying to figure out how to remove fragment(s) from backstack and you are using Android Jetpack Navigation component you could just use app:popUpTo and app:popUpToInclusive in action node of navigation graph xml to specify fragments that you want to pop out of back stack.
https://developer.android.com/guide/navigation/navigation-navigate#pop
Check this too
https://stackoverflow.com/a/51974492/4594986

What happens if the fragment that you want to remove is not on top of the stack?
Then you can use theses functions
popBackStack(int arg0, int arg1);
popBackStack(String arg0, int arg1);

Related

Android Back to previous fragment by popBackStack

I have 3 fragments: Home, A and B. Home and A are in mobile navigation menu.
User goes from A to B and then press back button. The issue that if I use getFragmentManager().popBackStack(); onCreateView of fragment A is calling and I have duplicating of content.
But if I use getActivity().onBackPressed(); it goes to Home fragment instead of A.
How to display fragment A by clicking back button without refreshing the view?
Here is how I make transaction from A to B
CertificateItemFragment certificateItemFragment = new CertificateItemFragment(item);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.nav_host_fragment, certificateItemFragment).addToBackStack(null);
fragmentTransaction.commit();
You need to know some premise before using FragmentManager.
in Activity there is FragmentManager and we should call it with getSupportFragmentManager(), getFragmentManager() is deprecated.
in Fragment there are multiple FragmentManagers called ParentFragmentManager and ChildFragmentManager, the final FragmentManager which is deprecated too. And ParentFragmentManager will be same as Activity's FragmentManager
getActivity().onBackPressed() will pull out fragment if any stack exists in Activity's FragmentManager
fragmentManager.popBackStack() will pull out fragment if any stack exists in Activity's or Fragment's FragmentManager rely on who calls
Base on above points
(1) If You want to hold fragments in Activity, you should call getSupportFragmentManager() in Activity, and call getParentManager() in Fragment, then onBackPressed() will pull out the fragment you last add to stack.
(2) If you want to hold fragments in a Fragment and Separated from Activity, you should call getChildFragmentManager() in Fragment, then activity.onBackPressed() will pull out the fragment which in Activity's fragment stack and ignore other fragments in Fragment's fragment stack.
For question will be same as case (1), and if you dont want re-create fragment, you should use add() instead of replace()
in Fragment A
CertificateItemFragment certificateItemFragment = new CertificateItemFragment(item);
FragmentManager fragmentManager = getParentFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.nav_host_fragment, certificateItemFragment).addToBackStack(CertificateItemFragment.TAG);
fragmentTransaction.commit();
now onBackPressed() will backstack from CertificateItemFragment to Pre-Fragment
In normal case navigation item list menu open fragment which are replace to each other but when we move inside of any item as like in your case move from fragment A to B,
In this case normally we use another activity on which create fragment.
If you do't want to use activity then, you just need to add fragment.
try below code that may help you
CertificateItemFragment certificateItemFragment = new CertificateItemFragment(item);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.nav_host_fragment, certificateItemFragment).addToBackStack(CertificateItemFragment.TAG);
fragmentTransaction.commit();

How to replace more than one fragments with another one in the back stack?

I have the following fragments in the back stack A, B, C. Then I want to add fragment D, but in the back stack I want to put A and D instead of A, B, C, D.
The problem is when I try to remove B and C with transaction.remove(), the backStackEntryCount is still the old one, which makes empty screens while pressing back. Is there a way to handle this?
When I use popBackStack()it goes to fragment A then comes D, which makes weird animation effects.
EDIT:
The question is not duplicate as it was marked, because this question is more about to control the order of several fragments in the back stack.
When adding to the Backstack, you can include a name to pop the backstack to when you're in a specific state. Based on the documentation for FragmentManager#popBackStack(string, int);:
Pop the last fragment transition from the manager's fragment back stack. If there is nothing to pop, false is returned. This function is asynchronous -- it enqueues the request to pop, but the action will not be performed until the application returns to its event loop.
Parameters
name String: If non-null, this is the name of a previous back state to look for; if found, all states up to that state will be popped. The POP_BACK_STACK_INCLUSIVE flag can be used to control whether the named state itself is popped. If null, only the top state is popped.
flags int: Either 0 or POP_BACK_STACK_INCLUSIVE.
So in this case you:
Add fragment A to the container and to the root backstack. Give it a name so you can reference it when popping.
Fragment A:
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, FragmentA, "FragmentA")
.addToBackStack("first")
.commit();
Add Fragment B to the container (replace or add, doesn't matter). Add to the backstack with a "null" or another name if you prefer. It must be different than "first".
Fragment B:
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, FragmentB, "FragmentB")
.addToBackStack(null)
.commit();
Add or replace with Fragment C like you would with Fragment B.
Fragment C:
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, FragmentC, "FragmentC")
.addToBackStack(null)
.commit();
Add or replace with Fragment D like you would with Fragment B and C.
Fragment D:
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, FragmentD, "FragmentD")
.addToBackStack(null)
.commit();
Then, in onBackPressed() you first check if "FragmentD" is in the stack. If it is, then pop all the way back to the root (which in this case is "first"). If not, just handle the back pressed like you normally would.
#Override
public void onBackPressed() {
FragmentManager fm = getFragmentManager();
if (fm.findFragmentByTag("FragmentD") != null) {
fm.popBackStack("first", 0); // Pops everything up to "first"
} else {
super.onBackPressed(); // Pops backstack like normal or leaves App if at base.
}
}
What this should do is allow your users to go "back" when they press the back button in Fragments A, B, or C. Then when they're finally in Fragment D, it will pop all the way back to A.
Fragment A
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, FragmentA, "FragmentA");
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Fragment B
fragmentTransaction.replace(R.id.fragment_container, FragmentB, "FragmentB");
Fragment C
fragmentTransaction.replace(R.id.fragment_container, FragmentC, "FragmentC");
Fragment D
fragmentTransaction.replace(R.id.fragment_container, FragmentD, "FragmentD");
Now your onBackPressed() in activity
#Override
public void onBackPressed() {
int lastStack = getSupportFragmentManager().getBackStackEntryCount();
try {
//If the last fragment was named/tagged "three"
if (getSupportFragmentManager().getFragments().get(lastStack).getTag().toString()=="FragmentD"){
getSupportFragmentManager().popBackStackImmediate();//skip c
getSupportFragmentManager().popBackStackImmediate();//skip B
Fragment fragment = getSupportFragmentManager().findFragmentByTag("FragmentA");
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, fragment);
transaction.commit();
return;
}
} catch (Exception e) {
e.printStackTrace();
}
super.onBackPressed();
}
make fragment_container for each of the fragment and keep on replacing the newest fragment's fragment_container..
Dont forget:
addToBackStack(null);

Back in fragment and nested fragment

i have app like this
one activity and inside it >
fragment a (loaded when run app also from menu can open it )
fragment b (open it from just menu)
fragment c (can open it from fragment a and also can open it from menu)
also inside fragment c there are 4 child fragments
in main activity(using navigation drawer as source) i call fragment a in oncreate like this
FragmentManager fragmentManager=getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.fragment_place,new First_Fragment()).addToBackStack("First").commit();
my problem is how to control back button to always back to fragment a and when fragment a is open close app
i was using addToBackStack(null) but is not what i want because will show all history of fragments that i opened
When adding fragment a to the back stack "addToBackStack(String name)" pass in a name.
Then listen for on back presses in your fragments
FragmentManager fm = getFragmentManager();
fm.addOnBackStackChangedListener(new OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
}
});
make sure to stop listening when each fragment is not being shown.
Then you can pop back to the named fragment added to the back stack
FragmentManager fm = getActivity()
.getSupportFragmentManager();
fm.popBackStack ("name", FragmentManager.POP_BACK_STACK_INCLUSIVE);
Make sure the rest of your fragment transactions are not added to the back stack. This should give you the behavior you want.
addToBackStack(String tag) is used to add the fragment to backstack and it contains the string as parameter. This parameter can be null or have some value.
If you pass null, it will add your fragment to backstack with tag null. addToBackStack(null) doesn't mean that your fragment is not added to backstack.
If you want your fragment will not be added to backstack, then just delete this line.
If you are adding your fragment to backstack and want it to be visible onBackPressed, then you can use
getSupportFragmentManager().popBackStackImmediate(/* Fragment TAG */,0);
CODE:- Try the below code and let me know.
Copy the below function in your main Activity.
/**
* function to show the fragment
*
* #param name fragment to be shown
* #param tag fragment tag
*/
public void showFragment(Fragment name, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
// check if the fragment is in back stack
boolean fragmentPopped = fragmentManager.popBackStackImmediate(tag, 0);
if (fragmentPopped) {
// fragment is pop from backStack
} else {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, name, tag);
fragmentTransaction.addToBackStack(tag);
fragmentTransaction.commit();
}}
Show fragment using the below code.
showFragment(yourFragment, yourFragmentTag);
In mainActivity onBackPressed.
#override
public void onBackPressed(){
FragmentTransaction fts = getSupportFragmentManager().beginTransaction();
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() >= 2) {
// always show your fragment a here
showFragment(new FragmentA(), FragmentA.class.getSimpleName());
} else {
// finish your activity
}
}

Retain same instance of Fragment on configuration Change

I have the following issue. My application has 2 fragments, the first one that is executed show a list of items, when you tap on an item, shows a detail fragment.
I also have a NavigationDrawerActivity, with the following methods to handle the stack of fragments.
private void changeFragment(int pos, Fragment fragment, boolean addToBackStack) {
Fragment f = getSupportFragmentManager().findFragmentByTag(String.valueOf(pos));
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
mCacheServices.setmFragmentTag(String.valueOf(pos));
ft.replace(R.id.container, fragment, String.valueOf(pos));
if ((addToBackStack)) {
ft.addToBackStack(STACK_KEY);
}
ft.commit();
}
The problem i am facing is when i am in the DetailFragment, and i rotete the device, it goes to the ItemList fragment, which is the firs fragment excuted by the app. What can i do to mantain on the DetailFragment when i rotate the device?
Thanks!
you can save the state of the fragment and then re-create it. Go through this for more details
http://developer.android.com/guide/topics/resources/runtime-changes.html
hope this helps.

Android FragmentManager calls onCreateView while clearing fragments

I have these methods for fragment manipulation:
public void replaceFragment(Fragment fragment) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.main_frame, fragment);
fragmentTransaction.commit();
fragmentManager.executePendingTransactions();
}
public void addFragment(Fragment fragment, boolean addToBackStack) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.main_frame, fragment);
if (addToBackStack) {
fragmentTransaction.addToBackStack(fragment.getClass().getName());
}
fragmentTransaction.commit();
fragmentManager.executePendingTransactions();
}
public void clearFragments() {
if (fragmentManager.getBackStackEntryCount() > 0) {
FragmentManager.BackStackEntry first = fragmentManager.getBackStackEntryAt(0);
fragmentManager.popBackStack(first.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
Scenario:
I start with fragment A, when activity is created. addFragment(new A, false);
Then I add fragment B. replaceFragment(new B);
I add another fragment C. addFragment(new C);
In C fragment I want to reset application to A fragment. I call clearFragments(); addFragment(new A, false);
The problem is that B fragment onCreateView is called after pressing button on C fragment that clears fragments and adds A fragment. I don't see the fragment, but it starts background thread that crashes my app. I need to clear all fragments without calling their onCreateView() methods.
Is this expected behaviour, or am I doing something wrong?
Actually in such a scenario you will always return to the fragment B.
Step by step:
Adding A.
Replacing A with B <- after this step you don't have A within your activity.
Adding C. Here you are adding another fragment and telling FragmentManager to preserve current state of fragments to the back stack. Your current state at this point is fragment B. Then fragment C is added.
When you call clearFragments, FragmentManager returns to the first saved state - and this state is fragment B.
Maybe you forgot to mention something? I tried to put several entries in a back stack and then reset to the first state - onCreateView is called once, only for the first fragment.

Categories

Resources