I need to be able to go through fragments like this: A->B->C1
When I'm on C1 I need to replace it with C2, but when I press back I should be taken back to B instead of C1.
I'm able to do this like this:
String tag = fragment.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
manager.popBackStack();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.mainActivity_container, fragment, tag);
ft.addToBackStack(tag);
ft.commit();
The problem is that while I'm changing from C1 to C2, fragment B gets it's onCreateInnerView method called.
I need to be able to do something like this: A->B->C1->C2->C1->C2 and by pressing back in C2 I should go to B
How to execute replacement without recreating fragment B?
You can handle the onBackPressed() event and replace whatever is the current fragment to fragment B.
First thing You don't need to popupstack while moving to fragment. Because It will call your last stack's fragment onViewCreated method.
While moving to B -> C1 , B -> C2 and C1 -> C2 :
Need to add Back stack null
Remove popupbackstack
I hope it will works for you.
Try this might be help you,
Just replace two line ,
manager.popBackStack();//Remove this line
ft.replace(R.id.mainActivity_container, fragment);//Remove 3rd param in this line
ft.addToBackStack(tag);//Remove this line
EDITED
you can also try this
public void switchContent(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); ++i) {
Fragment currentFragment = this.getSupportFragmentManager().findFragmentById(R.id.mainActivity_container);
if(currentFragment != B){
fragmentManager.popBackStack();
}
}
fragmentTransaction.replace(R.id.mainActivity_container, fragment).commit();
}
Related
I have a Activity where I load 3 fragments one after another
FragmentA
FragmentB
FragmentC
Flow is Like this I have used Adding fragment one above another
Start-Activity -----> Load FragmentA ----> Load FragmentB ----> Load FragmentC
What I am trying to do now is:
Now assuming now FragmentC is the top fragment shown
I want to find the FragmentA from the stack and just show it instead of creating a fragmentA all over again
Code I have used to add fragmentA is for Example:
Fragment fragment = null;
FragmentTransaction fragmentTransaction = null;
fragment = new FragmentA();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.addToBackStack(FragmentA.class.getSimpleName());
fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
fragmentTransaction.add(R.id.container, fragment, FragmentA.class.getSimpleName());
if(fragment!=null && fragmentTransaction!=null){
fragmentTransaction.commitAllowingStateLoss();
}
Look at FragmentManager.getBackStackEntryAt method, from there you can go back to any fragment of your history…
Loop through the back stack entries, meanwhile, if you find any matching Fragment by id or tag, just pop back stack inclusive with fragment A's name/tag so that the fragment A is just displayed rather than adding once again.
The example code to retrieve a fragment by Tag would look like this
FragmentA frA = getSupportFragmentManager().findFragmentByTag(FragmentA.class.getSimpleName());
and if it's not null you can reuse it in your container
if(frA != null) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
//you could add this transactionn to backstack again here if you want to be able to pop it later
fragmentTransaction.add(R.id.container, frA, FragmentA.class.getSimpleName());
} else {
//if your fragment is null as it was destroyed previously you can create a new one here
}
You are giving a tag, fragmentTransaction.addToBackStack(FragmentA.class.getSimpleName()); to the transaction , you can use the same tag to find the fragment in the backstack, before creating a new frgament, check the backstack for the fragment using findFragmentByTag on the fragment manager, if it exist, the method returns the fragment otherwise null
try it!
val fm: FragmentManager? = fragmentManager
for (entry in 0 until fm.getBackStackEntryCount()) {
Log.i(TAG, "Found fragment: " + fm.getBackStackEntryAt(entry).getId())
}
I am trying to do the following use case in Android Fragments. I have 2 fragments.
Fragment A -> Fragment B
When a user does something in Fragment B, I want to have the back stack as follows
Fragment A -> Fragment C. So, when the user presses back I want the user to go back to Fragment A.
I have tried the following
mFragmentManager.popBackStackImmediate();
FragmentTransaction fragmentTransaction = fMgr.beginTransaction()
.replace(R.id.base, Fragment_C, "1")
.addToBackStack(null)
.commitAllowingStateLoss();
The problem here is that I can see Fragment A for a short period of time before Fragment C is shown
If I do the following
mFragmentManager.popBackStackImmediate();
FragmentTransaction fragmentTransaction = fMgr.beginTransaction()
.replace(R.id.base, Fragment_C, "1")
.addToBackStack(null)
.commitNowAllowingStateLoss();
I get the error
This transaction is already being added to the back stack
I can get Fragment C to show up if I do this BUT
mFragmentManager.popBackStackImmediate();
FragmentTransaction fragmentTransaction = fMgr.beginTransaction()
.replace(R.id.base, Fragment_C, "1")
.commitNowAllowingStateLoss();
This works and I don't see Fragment A and see Fragment C but the back button takes the user out of the application. So, is it possible that we can pop the back stack of the fragment and then add another fragment to the back stack w/o showing Fragment A AND the back button takes the user back to Fragment A
Here is an easy method to add fragments to fragments or to adapters within fragments...
from your base activity, make your fragment manager static. assume this activity is called dashboard.
static FragmentManager support;
Don't forget to initialize this in onCreate.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_dashboard);
support = getSupportFragmentManager();
define your new fragment inside your adapter or fragment.
users_item_fragment dialog = new users_item_fragment();
//also, let's add some data...
Bundle args = new Bundle();
args.putString("device", devicesList.get(position));
use the following method to add the fragment easily wherever you would like
//pick an easily remembered tag
public void replace(Fragment fragment, String tag){
FragmentManager man = dashboard.support;
FragmentTransaction fragt = man.beginTransaction();
if(!fragment.isAdded()) {
dashboard.lastTag = dashboard.fragtag;//not needed, but helpful w/ backpresses
fragt.add(R.id.fragment_container, fragment, tag)
.hide(man.findFragmentByTag(fragtag)).commit();
dashboard.fragtag = dashboard.tag;//not needed, but helpful w/ backpresses
}
if(fragment.isAdded() && fragment.isHidden()) {
dashboard.lastTag = dashboard.fragtag;//not needed, but helpful w/ backpresses
fragt.show(fragment);
fragt.hide(man.findFragmentByTag(fragtag)).commit();
dashboard.fragtag = dashboard.tag;//not needed, but helpful w/ backpresses
}
}
To implement this with backpresses working correctly, add this in you onBackPress method of your main activity:
#Override
public void onBackPressed() {
FragmentManager man = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = man.beginTransaction();
fragmentTransaction.hide(getSupportFragmentManager().findFragmentByTag(fragtag))
.show(getSupportFragmentManager().findFragmentByTag(lastTag)).commit();
fragtag = lastTag;// holds the last fragment
}
}
It's easy to see the logic here and easy to manipulate back press events using this.
I have three fragments A、B、C , my process is A to B to C .
I want that when i change to C and call back to B , B can switch to A immediately.
So i set the switch fragment function on onResume on B .
But the result is that i always land up on A fragment , because when i change to B the function onResume be called
I know i can remove transaction.addToBackStack(null); then i can C to A
, but it will cause some bug in my project , so i try to keep it.
Is any function only start when C call back to B ?
Thanks in advance.
Below is my switch function:
private void switchFragment(Fragment fragment) {
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.mainFrame, fragment, null);
transaction.addToBackStack(null);
transaction.commit();
}
I try to add onResume on fragment B , but it will be A to B , and then B go back to A immediately.
#Override
public void onResume() {
super.onResume();
//go back to fragment A
switchFragment(NewHomepage.newInstance());
}
your fragment transition code should be:
private void switchFragment(Fragment fragment) {
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.mainFrame, fragment, null);
transaction.addToBackStack("fragment_name_tagX");
transaction.commit();
}
above, X is your fragment. After you navigate from fragment A to C using above function, your stack will have every fragment with name 'fragment_name_tag' that you assign.
#Override
public void onBackPressed() {
FragmentManager fm = getActivity().getSupportFragmentManager();
int count = fm.getBackStackEntryCount();
if (count != 0) {
fm.popBackStack ("fragment_name_tagB",FragmentManager.POP_BACK_STACK_INCLUSIVE);
} else {
super.onBackPressed();
}
}
So when you now press back button from fragment C, it will go on fragment A.
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);
I have a problem about removing a specific fragment from back stack.My scenario is like this.Fragment-1 is replaced with Fragment-2 and then Fragment-2 is replaced with Fragment-3.
Calling order; Fragment-1-->Fragment-2-->Fragment-3.
When Fragment-3 is on the screen and then back button is clicked, i want to go
Fragment-1.That means i want to delete Fragment-2 from back stack.
How to do this ?
In the backstack you don't have Fragments, but FragmentTransactions. When you popBackStack() the transaction is applied again, but backward. This means that (assuming you addToBackStackTrace(null) every time) in your backstack you have
1->2
2->3
If you don't add the second transaction to the backstack the result is that your backstack is just
1->2
and so pressing the back button will cause the execution of 2->1, which leads to an error due to the fragment 2 not being there (you are on fragment 3).
The easiest solution is to pop the backstack before going from 2 to 3
//from fragment-2:
getFragmentManager().popBackStack();
getFragmentManager().beginTransaction()
.replace(R.id.container, fragment3)
.addToBackStack(null)
.commit();
What I'm doing here is these: from fragment 2 I go back to fragment 1 and then straight to fragment 3. This way the back button will bring me again from 3 to 1.
I had a very similar scenario to yours, my solution was just checking the amount of backStack transactions that I had.
If the transactions where more than 0 then I would simply pop it right away
so it would skip it when pressing back.
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStackImmediate();
}
...
fragmentTransaction.replace(R.id.main_fragment, newFrag, MAIN_FRAGMENT_TAG);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.commit();
This would successfully:
A -> B (back pressed) -> back to A
A -> B -> C (back pressed) -> back to A
The only downside that I see is that there is a quick flash where fragment A is displayed before going to fragment C.
If you are adding/launching all three fragments in the same activity, instead of the add(), use replace() (replace Fragment2 with Fragment3). The replace method removes the current fragment from backstack before adding the new one. If you are launching Fragment3 from a different activity, and thus you can't use replace(), remove Fragment2 from backstack before starting the new activity (which adds Fragment3):
// in Fragment2, before adding Fragment3:
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.remove(this) // "this" refers to current instance of Fragment2
.commit();
fragmentManager.popBackStack();
// now go ahead and launch (add) fragment3
// if fragment3 is launched from a different activity,
// start that activity instead
fragmentManager.beginTransaction()
.add(R.id.a_container_view_in_activity, new Fragment3(),
Fargment3.FRAGMENT3_ID)
.commit();
Code for Fragment A -> Fragment B:
Add Fragment A in BackStack of Fragments
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_layout_container, new fragmentB());
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Code for Fragment B -> Fragment C:
Do not Add Fragment B in BackStack of Fragments
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_layout_container, new fragmentC());
fragmentTransaction.commit();
It will works in this way: A -> B -> C and while returning C-> A as you excepted.
Hope it will help you.
for (int i = 0; i < mFragmentManager.getBackStackEntryCount(); i++) {
currentFragment = mFragmentManager.getFragments().get(i);
if (!(currentFragment instanceof ParticularFragment))
mFragmentManager.popBackStack();
else
break;
}