My app has this flow
Activity (TabLayout) -> Fragment 1 -> Fragment 2 -> Fragment 3
When I either press back on Fragment 2 or Fragment 3, I already go back to Fragment 1. I want to go back to Fragment 2 if I am on Fragment 3.
From fragment 1 to fragment 2
Frag2 childFrag = new Frag2();
getChildFragmentManager().beginTransaction().add(R.id.frag2_frame, childFrag)
.addToBackStack(null)
.commit();
From fragment 2 to fragment 3
Frag3 childFrag = new Frag3();
getChildFragmentManager().beginTransaction().add(R.id.frag3_frame, childFrag)
.addToBackStack(null)
.commit();
onBackPressed in Activity
// a fragments list for tablayout
ArrayList<Fragment> fragments = new ArrayList<>();
public void onBackPressed(){
fragments.get(selectedTab).getChildFragmentManager().popBackStack();
}
Try adding backStack
Frag2 childFrag = new Frag2();
getFragmentManager().beginTransaction().add(R.id.frag2_frame, childFrag)
.addToBackStack("Frag2 ")
.commit();
Once fragment 1 is added to the Activity, I believe you'll need to use getChildFragmentManager() to add additional fragments from a fragment. This method is available to classes that extend a Fragment.
Edit
As you've said you are using getChildFragmentManager(), I would use .beginTransaction().replace(...) instead of .beginTransaction().add(...).
Try to find the deepest fragment. Then popBackStack right there.
List<Fragment> fs2 = fragments.get(selectedTab).getChildFragmentManager().getFragments().get(0).getChildFragmentManager().getFragments();
FragmentManager f = fragments.get(selectedTab).getChildFragmentManager();
while (fs2.size() > 0) {
fs2 = fs2.get(0).getChildFragmentManager().getFragments();
f = f.getFragments().get(0).getChildFragmentManager();
}
f.popBackStack();
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())
}
In my android activity I am using multiple fragments, I am successfully switching these fragments according to my requirement but problem is that when I am switching Fragment 1 to Fragment 2 and back from Fragment 2 to Fragment 1, Fragment 1 not showing previous data Fragment 1 start from stretch, but I want to show previous data same as I was selected.
Here is my code for go Fragment 1 (fragment_search_customerProfile) to Fragment 2 (fragment_CustomerInfo):
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.filterFram, new fragment_Search());
FrameLayout layout = (FrameLayout) rootView.findViewById(R.id.cpFrame);
layout.removeAllViewsInLayout();
transaction.remove(new fragment_search_customerProfile()).commit();
Here is my code for back Fragment 1 (fragment_search_customerProfile) fromFragment 2 (fragment_CustomerInfo):
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
Log.d("fragment_Search", fromSearch + "");
transaction.replace(R.id.custIndoFram, new fragment_search_customerProfile());
FrameLayout layout = (FrameLayout) rootView.findViewById(R.id.custIndoFram);
layout.removeAllViewsInLayout();
transaction.remove(new fragment_CustomerInfo()).commit();
Can anyone explain me how can I stay save my fragment data?
Instead of transaction.replace(R.id.filterFram, new fragment_Search());
you can use transaction.add to add fragment while having the data of first one
transaction.add(R.id.filterFram, new fragment_Search());
transaction.addToBackStack(null);
From fragment two you can use .show instead of replace to show the first fragment
and hide fragment two from the first one. transaction.show(getSupportFragmentManager().findFragmentByTag("firstFragmentTag")).commit();
You can simple keep the instances of the fragments, so in your Activity you would have fields like that.
private Fragment fragment1;
private Fragment fragment2;
And now you can use the sexy replace() option,
To add Fragment1
if (fragment1 == null) {
fragment1 = new fragment_Search();
}
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.filterFram, fragment1);
and same for Fragment2
And you can hold the state of the fragment (data it retrieves) in the Fragment itself, and in your onViewCreated() you check, if the state is not null, you update the view immediately.
class Fragment1 extends Fragment {
private State state;
public void onViewCreated(View v) {
if (state != null) {
// Update UI here
}
}
}
UPDATE
Using your own code
private Fragment fragment1; // This is a field outside of below method
if (fragment1 == null) {
fragment1 = new fragment_Search();
}
FragmentManager manager = getChildFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.filterFram, fragment1).commit();
FrameLayout layout = (FrameLayout) rootView.findViewById(R.id.cpFrame);
layout.removeAllViewsInLayout();
Going to fragment2 should be the same, and this code is used to go to Fragment1 and Fragment2, doesn't matter if you are going back or first time.
You have to add fragment instead of remove or replace
What I am trying to do is at onBackPressed I want create a new object of the top Fragment so I am trying this code:
if (getFragmentManager().getBackStackEntryCount() > 1) {
FragmentManager.BackStackEntry backEntry =getFragmentManager().getBackStackEntryAt(getFragmentManager().getBackStackEntryCount()-1);
String Back_fragment_name=backEntry.getName();
if(Back_fragment_name.equals("Frag1")){
Frag1 frag = new Frag();
getFragmentManager().beginTransaction().replace(R.id.RR, frag , "Frag1").commit();
}else if(Back_fragment_name.equals("Frag2")){
Frag2 frag = new Frag2();
getFragmentManager().beginTransaction().replace(R.id.RR, frag , "Frag2").commit();
}else if(Back_fragment_name.equals("Frag3")){
Frag3 frag = new Frag3();
getFragmentManager().beginTransaction().replace(R.id.RR, frag , "Frag3").commit();
}
//getFragmentManager().popBackStack(); //Wrong
} else{
super.onBackPressed();
}
The problem is I think I should then remove the Top Fragment from BackStack but using popBackStack is wrong because it shows the the top fragment and i only need to remove it from BackStack.
any Ideas?
What I was trying to do is to implement the onBackPressed, that is pressing back button will transact the previous fragment, But I did not want to transact the previous Instance but a new one so I was using the Question's code. to do that I was adding fragments to BackStack in order to know which fragment is previous [the top one of the Backstack] then creating a new instance of it. So i was getting it's name then create it's new instance then delete it from the BackStack [Thats was the problem, I could not be able to delete it from backstack].
Finally I figured out that I do not need to use BackStack at All to know what are the previous fragments I just used an ArrayList of String to save every transacted Fragment's Name or tag so for example when transacting Frag1 I do this:
Frag1 frag1 = new Frag1 ();
fragmentTransaction.replace(R.id.RR, frag1 , "frag1").commit();
mPreviousFragmentsNames.add("frag1");
and onBackPressed will be like this:
if(!Removed_Last_Fragment_Name_From_backList){
// a boolean value, removing this will cause a bad behavior
// that is if back button pressed it transact current shown fragment first then u need to press back again to go to previous one
mPreviousFragmentsNames.remove(mPreviousFragmentsNames.size()-1);
Removed_Last_Fragment_Name_From_backList = true;
}
if (!mPreviousFragmentsNames.isEmpty()) {
String Back_fragment_name = mPreviousFragmentsNames.get(mPreviousFragmentsNames.size()-1);
if(Back_fragment_name.equals("Frag1")){
Frag1 frag = new Frag();
getFragmentManager().beginTransaction().replace(R.id.RR, frag , "Frag1").commit();
}else if(Back_fragment_name.equals("Frag2")){
Frag2 frag = new Frag2();
getFragmentManager().beginTransaction().replace(R.id.RR, frag , "Frag2").commit();
}else if(Back_fragment_name.equals("Frag3")){
Frag3 frag = new Frag3();
getFragmentManager().beginTransaction().replace(R.id.RR, frag , "Frag3").commit();
}
} else{
super.onBackPressed();
}
and It works Great, as Needed !
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;
}
Currently, I am working on a new android project.
In my project, I have 2 FrameLayout (called F1, F2) which are used as containers of Fragment.
On "F1" I will add a fragment (Like navigation drawer fragment, menu item....).
On "F2" I called its MainContainer - It's will use container all "Main" fragment. (I mean F2 will container fragment like HomeFragment, GameFragment......).
Here are some steps:
F1 -> add fragment f1;
F1 -> add fragment f2;
F2 -> add fragment g1;
F2 -> add fragment g2;
F1 -> add fragment f3;
Now I want to use the transaction fragment on Container F2.
I mean:
I can add/remove a fragment on Container F2. But in android Fragment Manager will manager all Fragment f1..f3, g1..g2;
So I called popBackStack -> It's will remove fragment f3. (But I want to remove g2).
For any task like addFragment, remove, replace fragment I only want to do Fragment in Container F2. (F1 will be never changed).
Please help!
When you adding the fragment, you can use addToBackStack(String name) and disallowAddToBackStack() to control the back stack of FragmentManager.
For your requirement, you should use the code like this to add f1, f2, and f3:
getFragmentManager().beginTransaction()
.add(R.id.F1, [your fragment instance])
.disallowAddToBackStack()
.commit();
And use the code like below to add g1 and g2:
getFragmentManager().beginTransaction()
.add(R.id.F2, [your fragment instance])
.addToBackStack(null)
.commit();
Check the officail document of addToBackStack(String name) and disallowAddToBackStack() for more details.
You should use child fragment when adding child to that fragment.
public void addFragB() {
FragmentManager childFragMan = getChildFragmentManager();
FragmentTransaction childFragTrans = childFragMan.beginTransaction();
FragmentClassB fragB = new FragmentClassB();
childFragTrans.add(R.id.fragA_LinearLayout, fragB);
childFragTrans.addToBackStack("B");
childFragTrans.commit();
}
And when removing the fragment
public void backPressed() {
int childCount = parentFragment.getChildFragmentManager().getBackStackEntryCount();
if (childCount == 0) {
// it has no child Fragment
} else {
// get the child Fragment
FragmentManager childFragmentManager = parentFragment.getChildFragmentManager();
// removing the child Fragment from stack
childFragmentManager.popBackStackImmediate();
}
}