Android popBackStack shows fragment not added to the backstack - android

MainFragment launches FragA that launches FragB that lunches FragC.
Back button press on FragB should go to FragA and back press on FragC should also go to FragA.
FragB is the only one where isToAddToBackStack is false.
childFragmentManager.commit {
replace(containerViewId, fragment, fragment::class.java.name)
if (isToAddToBackStack) {
addToBackStack(backStateName)
}
}
Whenever the back button is pressed on FragC, FragB is shown:
if (childFragmentManager.backStackEntryCount > 1) {
childFragmentManager.popBackStack()
return
}
What's the better way to achieve the navigation that I'm expecting?

Fragment transactions can involve two different types of tags. The one that most Android developers are familiar with is the Fragment tag, which you can use to find a specific Fragment in your FragmentManager later via findFragmentByTag(). This is useful for finding a Fragment when your application is in a particular state, but keep in mind that the Fragment needs to be added to your FragmentManager. If you have removed() or replaced() a Fragment and haven’t added it to the back stack, you won’t be able to find it.
The other type of tag is the BackStackRecord’s name passed into addToBackStack(). This name identifies a particular back stack record, which is a record of what occurred in a particular transaction. popBackStackImmediate() and its counterparts have variants that accept a back stack record name to pop the back stack to a particular state.
//fm is FragmentManager
// Fragment a is on the screen
Fragment a = new A_Fragment()
fm.beginTransaction()
.remove(null /*no fragments in R.id.content*/)
.add(R.id.content, aFragment, "fragment-a")
.commit();
// user wants to go from A to B
Fragment bFragment = new B_Fragment();
fm.beginTransaction()
.remove(fm.findFragmentById(R.id.content))
.add(R.id.content, bFragment, "fragment-b")
.addToBackStack("a")
.commit();
// user wants to go from B to C
fm.beginTransaction()
.remove(fm.findFragmentById(R.id.content))
.add(R.id.content, new C_Fragment(), "fragment-c")
.commit();

Related

How to handle fragment backstack

I'm having problem with fragment. Lets try to understand my issue, I have two fragment A and B. When app start with main activity,i start fragment A as you can see :
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new MusicFragment())
.commit();
When i click on a button, it starts fragment B
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new BarFragment())
.addToBackStack(null)
.commit();
Main problem is after starting fragment B,when i pressed back to go back to fragment A , Fragment A Recreated with new state.
I don' want to recreate fragment A. I only want to start fragment from old state where i left. How to fix it ?
Instead of calling the replace method you should be calling the add method with a subsequent call to addToBackStack with an argument of null. The add method adds the fragement to the stack with a null tag and calling the addToBackStack with an argument of null then the current fragment is stopped upon commit. If the method is not called then the current fragment is destroyed and recreated when coming back.
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container,new BarFragment())
.addToBackStack(null)
.commit();
You can clearly find it in the documentation quote saying this:
If you don't call addToBackStack() when you perform a transaction that
removes a fragment, then that fragment is destroyed when the
transaction is committed and the user cannot navigate back to it.
Whereas, if you do call addToBackStack() when removing a fragment,
then the fragment is stopped and is later resumed if the user
navigates back.
Here are some points that need to be taken care while creating fragments.
Check the backstack if the fragment is already created.
If it was created previously pop it from the backstack and put it on top so that it is visible to the user.
If fragment is not present in backstack crate it and store it in backstack.
Create a method like below which will handle such situation.
private void openFragment(Fragment fragment_to_be_opened){
String fragment_to_be_opened_name = fragment_to_be_opened.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
// fetching the fragment if it is present in backstack
boolean fragment_allready_present = manager.popBackStackImmediate (fragment_to_be_opened_name, 0);
if (!fragment_allready_present){ //fragment is not present in backstack so create it and save the name in //backstack
FragmentTransaction fragment_trasition = manager.beginTransaction();
fragment_trasition.replace(R.id.fragment_container, fragment_to_be_opened);
fragment_trasition.addToBackStack(fragment_to_be_opened_name);
fragment_trasition.commit();
}
}
Now call this method from the activity to open a new fragment like
// create instance of fragment and pass it to the open fragment method
Fragment myFragment = new myFragment();
openFragment(myFragment);

Clear fragment backstack without invoking onCreateView of popped fragments

My app has 3 UI levels, each level has its own fragment, A -> B -> C.
I wish to optionally allow my app user to navigate straight to the top level fragment i.e. from C -> A without invoking B.
i.e., I still want to allow the user to go from C->B if they press the back button, but in the C fragment, I have a "Home" button, which takes them directly to A. This is the operation where I want to flush the backstack.
Additionally, I want the user to be able to go from B->A using the back button, hence I'm adding A and B both to the backstack.
I have tried the options from this SO post but in each case, onCreateView gets invoked for each fragment, even if I use fm_.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
Also checked this thread, it seems to recommend using popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
Can anyone please suggest a way to pop the entire backstack without having the onCreateView called on popped fragments?
Dont add backStack line for going from fragment B->C
while going from A->B use
HighlightFragment highlightFragment=new HighlightFragment(FirstReaderScreen.this);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.LL_Fragment, highlightFragment) // LL_Fragment is container
.addToBackStack(null)
.commit();
while going B->C use
HighlightFragment highlightFragment=new HighlightFragment(FirstReaderScreen.this);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.LL_Fragment, highlightFragment)
.commit();
dont add line .addtobackstack(null)
This worked for me
i estimate you're using replace fragment.
try to use begintransaction.hide / show to manage many fragment inside 1 activity if you don want to popback
//on Activity
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(containerId, new FragmentA(), fragmentA.getClass().getSimpleName());
fragmentTransaction.commit();
//inside fragment A to jump fragment without PopBackStack
getFragmentManager().beginTransaction().hide(this).commit();
getFragmentManager().beginTransaction().show(new FragmentB()).commit();
This is working for me when needing to clear the back stack for tab switches in BottomNavigationView
public void clearBackStack() {
int backStackEntryCount = mFragmentActivity
.getSupportFragmentManager()
.getBackStackEntryCount();
for (int i = 0; i < backStackEntryCount; i++) {
mFragmentActivity.getSupportFragmentManager().popBackStack();
}
}

Building fragment stack for One Activity Many Fragment App

My App has only one activity and lots of fragments.
In my activty's XML, I just have a FrameLayout on which I replace/add/hide/show various fragments.
Imagine Fragment A is the first fragment the user sees when they open the app.
Click something in Fragment A to launch Fragment B and click something in Fragment B to launch Fragment C.
So the navigation is can be illustrated as follows :
Fragment A --> Fragment B --> Fragment C
I want to launch the app and show Fragment C directly from notification.
However, how can I provide back navigation from Fragment C, as such clicking back would go to Fragment B and clicking back again go to Fragment A ?
i.e How can I inject the following stack structure ?
Fragment A <-- Fragment B <-- Fragment C
Yes, you can do this. Since support library v26 you can build the stack with fragments without significant cost.
In your activity make the following:
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, new FragmentA())
.addToBackStack("fragmentA")
.setReorderingAllowed(true)
.commit();
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, new FragmentB())
.addToBackStack("fragmentB")
.setReorderingAllowed(true)
.commit();
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, new FragmentC())
.addToBackStack("fragmentC")
.setReorderingAllowed(true)
.commit();
Keep in mind that FragmentA and FragmentB will behave in a bit different way after pressing back on FragmentC due to setReorderingAllowed. onCreateView won't be called for FragmentA and FragmentB after they were added to stack, only in FragmentC onCreateView will be called. For FragmentA and FragmentB only onCreate will be called.
What you can do is -
Use a notification intent in which you pass a string. In your main activity if you receive that string make a fragment stack of A, B and C.
Else if you don't get the intent just continue your flow as it is.

Fragment not displayed after screen rotation

I have one activity, ActivityA, and 3 fragments, FragmentA, FragmentB, and FragmentC.
To add FragmentA, I use replace, where fragment is a new instance of FragmentA:
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame_layout_fragment, fragment)
.commit();
In the onCreate method of ActivityA, I do a check for FragmentA creation:
if (savedInstanceState == null) {
FragmentA fragmentA = FragmentA.newInstance();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame_layout_fragment, fragmentA)
.commit();
}
To add FragmentB and FragmentC, I use add and addToBackStack, where fragment is either a new instance of FragmentB or FragmentC:
getSupportFragmentManager()
.beginTransaction()
.add(R.id.frame_layout_fragment, fragment)
.addToBackStack(null)
.commit();
I press the back button while on FragmentC, it shows FragmentB, and pressing the back button while on FragmentB shows FragmentA, as expected. While I was on FragmentC, I rotate the device, and FragmentC still shows, as expected.
However, when I navigate from FragmentC to FragmentA using the 2 presses of the back button, then rotate the device twice, portrait -> landscape -> portrait, add FragmentB, add FragmentC, then I rotate the device once, I expected FragmentC to show, but instead FragmentB shows. When I press the back button once, nothing happens. When I press it again, it navigates back to FragmentA. It seems like FragmentC is present in the back stack, but for some reason its not visible.
Why is FragmentC not visible in this scenario?
I think this answer to another question is your answer too: Difference between add(), replace(), and addToBackStack()
It says:
One more importance difference between add and replace is: replace
removes the existing fragment and adds a new fragment. This means when
you press back button the fragment that got replaced will be created
with its onCreateView being invoked. Whereas add retains the existing
fragments and adds a new fragment that means existing fragment will be
active and they wont be in 'paused' state hence when a back button is
pressed onCreateView is not called for the existing fragment(the
fragment which was there before new fragment was added). In terms of
fragment's life cycle events onPause, onResume, onCreateView and other
life cycle events will be invoked in case of replace but they wont be
invoked in case of add.

Back key to pop the Fragment shows overlapping fragments

I created a sample app to test this overlapping issue.
I have a fragment type, Fragment1, and I create a new instance of Fragment1 and add it to a FrameLayout in my activity at runtime. I add the fragment with the help of a few buttons.
Note: I have given each new instance of Fragment1 a different number(#1, #2, #3, etc.) to display on the UI to help me figure out which fragment I am viewing.
So.. here is what I do:
Click on Button 3, create new instance of Fragment1 and add it to Frame1.
Click on Button 4, create new instance of Fragment1 and add it to Frame1 and add it to the fragment backstack.
Repeat 1 and 2.
Repeat 1 and 2.
Now, I have fragments in this order: 1(#1),2(#2),1(#3),2(#4),1(#5),2(#6).
I press the back key when viewing fragment #6.
Back key press, UI displays (#5).
Back key press, UI displays (#3 AND #5),
Back key press, UI displays (#1, #3, AND #5)
It seems fragments are getting displayed ON TOP of each other.
WHY? Is there an overlapping issue? How can I clear out this overlapping issue. I thought this would be an issue on the compatibility library... but it is also on 3.0.
Code for adding fragments:
public int doFragmentChange(int cont1, Fragment frag1, String tag1, int cont2, Fragment frag2, String tag2,
boolean addToStack, String stackTag) {
FragmentManager fm = getFragmentManager();// getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
if (frag1 != null) {
ft.replace(cont1, frag1, tag1);
}
if (frag2 != null) {
ft.replace(cont2, frag2, tag2);
}
// add fragment to stack
if (addToStack)
ft.addToBackStack(stackTag);
return ft.commit();
}
If you perform two add calls one after the other (two commit calls) then yes the fragments will appear overlaid, one on top of the other effectively.
So (for new example) if say you replace frag1 with frag2 and then frag3 with frag4 in the same frame with no backstack transaction then I would expect frag2 and frag4 to be overlaid.
Furtheremore there is also a potential issue in your chaining of replace. You should call a separate commit for each. See Android — Replace Fragment Back Stack With New Stack?.
Just override the onBackPress() or onKeyUp and remove the top fragment.

Categories

Resources