I want to know
getFragmentManager()
.beginTransaction()
.replace(
R.id.main,
Fragment.instantiate(LoadingScreen.this,
"com.myapp.fragments.fragment1",
bundle)).commit();
and then later on we call
getFragmentManager()
.beginTransaction()
.replace(
R.id.main,
Fragment.instantiate(LoadingScreen.this,
"com.myapp.fragments.fragment2",
bundle)).commit();
What happens to the Fragment1 view ? Will it be destroyed automatically, do we have to manage any garbage collection on this ?
Kind Regards
According to the note on Android Developers:
When you remove or replace a fragment and add the transaction to the back stack, the fragment that is removed is stopped (not destroyed). If the user navigates back to restore the fragment, it restarts. If you do not add the transaction to the back stack, then the fragment is destroyed when removed or replaced.
Related
the common practice to replace a fragment by another fragment is:
getActivity().getSupportFragmentManager()
.beginTransaction()
.setReorderingAllowed(true)
.replace(R.id.fragment_holder, ItemFragment.newInstance(bundle),"tag")
.addToBackStack(null)
.commit();
the documentation for commit() method says:
Schedules a commit of this transaction. The commit does not happen immediately; it will be scheduled as work on the main thread to be done the next time that thread is ready.
now imagine I call getActivity.getSupportFragmentManager.findFragmentByTag("tag"); right after the commit method mentioned above. since the commit does not happen immediately, is it possible that it returns null?
I don't think it can return null if that was the case the documentation for findFragmentByTag("tag") would have stated that.
Also the method is from FragmentManager class so all it cares about whether or not is contained in the fragment manager the commit() function is from FragmentTransaction class a fragment can be in the back stack of fragment manager even if it is not commited so that is why the function findFragmentByTag("tag") will return fragment and will return null only if the fragment with that tag was never added to the fragment manager
getSupportFragmentManager()
.beginTransaction()
.setReorderingAllowed(true)
.replace(R.id.fragment_holder, fragmentB, "tag")
.addToBackStack(null)
above code adds the fragment to the back stack of fragment manager of host activity you can do just that and find this fragment in the fragment manager, commit is not required.
I would say it is possible, however I feel the real problem here is the design you are using. If Fragment A needs to introduce/replace Fragment B, then the correct design is to let the Activity handle the Fragment management. Fragments should not be directly adding/replacing other Fragments.
If this is all handled within the the Activity, you can easily keep a reference to the new Fragment, meaning you don't need to findFragmentByTag("tag") because you already have a reference to it when you created/replaced.
Activity
/ \
Fragment A Fragment B
Fragment A and Fragment B should not directly influence each other.
// In Activity ...
public replaceFragment() {
Fragment fragmentB = ItemFragment.newInstance(bundle);
getSupportFragmentManager()
.beginTransaction()
.setReorderingAllowed(true)
.replace(R.id.fragment_holder, fragmentB, "tag")
.addToBackStack(null)
.commit();
// You now have fragmentB no need to getSupportFragmentManager.findFragmentByTag("tag")
fragmentB.doSomething();
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);
If I have an Activity and I add fragment1, hide fragment1, add fragment2, hide fragment2, then use
fragmentManager.beginTransaction()
.replace(R.id.content, fragment3)
.commit();
to now replace in fragment3, what happens to fragment1 and fragment2? Do they get destroyed or something or are they still there? (this is all within the same container, R.id.content)
Unless you add the transaction to backstack, all the the fragments in the container are getting destroyed
If the fragment was added to the back stack, the fragments object instances will have onDestroyView() called, and the data required to reconstruct the fragment is still part of the fragment back stack, so that when fragments are popped off the stack, the prior fragments can be reconstituted and displayed again (similar to activities that may have been destroyed in the activity stack). If the fragment was not part of the back stack, it is simply destroyed because the user can never come back to it.
Having a flow to add fragment and later replace() fragment. All fragment are dynamically added, but not all call addToBackStack().
getSupportFragmentManager().beginTransaction()
.add(R.id.frgment_holder, frgmtA, frgmtA.NAME)
.commit();
and in somewhere it could add another, like:
getSupportFragmentManager().beginTransaction()
.replace(R.id.frgment_holder, frgmtB)
.addToBackStack(frgmtB.NAME)
.commit();
the replace() with the frgmtB will remove the frgmtA from the container R.id.frgment_holder.
if press back button at this state, it will pop the frgmtB. But will it recreate the frgmtA even if it did not call addToBackStack() when did the adding?
if in the flow of adding fragment into same container with a serials mixed add() and replace() calls, and someone call addToBackStack() but someone do not, how would the back button behavior?
EDIT:
after
getSupportFragmentManager().beginTransaction()
.replace(R.id.frgment_holder, frgmtB)
.addToBackStack(frgmtB.NAME)
.commit();
will the
getSupportFragmentManager().findFragmentByTag(frgmtA.NAME);
still find the the frgmtA? what if when add frgmtA also called addToBackStack();
The doc says "This first searches through fragments that are currently added to the manager's activity; if no such fragment is found, then all fragments currently on the back stack are searched."
The case would be
adding frgmtA; not call add to stack; UI state changed here;
(what if the frgmtA is not dynamically added by add(), but sepcified in layout file with class="frgmtA"?).
replace() with frgmtB; addToStack();
replace() with frgmtC; addToStack();
then if the stackTop is frgmtC, would like the back button press to bring back the first frgmtA with its last UI state.
1.
.add(R.id.frgment_holder, frgmtA, frgmtA.NAME)
.commit();
.replace(R.id.frgment_holder, frgmtB, frgmtB.NAME)
.addToBackStack(frgmtB.NAME)
.commit();`
the replace will remove the frgmtA from the the holder and its onDestroyView will be called (but since it is referenced in the backstack’s transaction data, frgmtA is NOT destroyed). And the frgmtB will be added to the holder.
Since the frgmtA is not destroyed, the
getSupportFragmentManager().findFragmentByTag(frgmtA.NAME);
will find it.
after then, press on back button, it will pop the top transaction from the backStack, then reversing the transaction. i.e. remove frgmtB from the holder and add frgmtA back to the holder. Since there is no more reference to frgmtB its onDestroy is called.
2.
in the case of
add frgmtA;
replace() with frgmtB; addToStack();
replace() with frgmtC; addToStack();
If want to back press to jump to frgmtA, need to override onBackPressed(), in it
do
popBackStack(frgmtB.NAME, POP_BACK_STACK_INCLUSIVE),
which will toss out the transaction in the stack above stack entry named frgmtB.NAME and do reverse the transaction on it, which will add back the frgmtA in the holder.
Unless you interfere with the Back button logic using onBackPressed() or addOnBackStackChangedListener(), things are straight forward
if you add a fragment through addToBackStack(), it will be removed when Back is tapped
if there are no other fragments left in the back stack, app will be closed
try this anyone line as your demand
its 4 differnt lines code you have checked
getActivity().getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
getActivity().getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.framelayout, fragment);
transaction.addToBackStack(null);
getActivity().getSupportFragmentManager().popBackStack();
I'm trying to decide and show a fragment in activity's onResume method, but in case a previously added fragment is chosen again, then the activity goes blank.
Sample code (with one fragment):
#Override
protected void onResume(){
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.replace(R.id.myLayout, fragA);
trans.commit();
getSupportFragmentManager().executePendingTransactions();
}
With above code, when the activity is created for the first time, it shows fragA correctly, but in case I press Home Key and then switch back to my activity (in order to provoke onResume again), it all goes blank (seems like fragA is removed).
Is replacing a previously added fragment removes itself? or how not to loose a fragment if it is replaced by itself?
You can't replace a fragment with itself. The first half of a replace is a removal of the previous fragment at that id. Once a fragment is removed it can no longer be added or used by the fragment manager (so the add portion of the replace will not work properly).
Depending on your use case, you have two options:
Create a new fragment instead of reusing the existing instance
Use some other method to see if its necessary to replace your fragment
Finally, you probably don't need to call executePendingTransactions.
You can try:
if( !(getSupportFragmentManager().findFragmentById(R.id.myLayout) instanceof FragmentA) ) {
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.replace(R.id.myLayout, fragA);
trans.commit();
}
And I assume that fragA is FragmentA class object.
Finally, I had to put a check before replacing fragments. In case, an (already added) fragment is requested for replace again, I had to check if its already added then ignore the replacement, else proceed. For example:
#Override
protected void onResume() {
if (!fragA.isAdded()) {
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.replace(R.id.myLayout, fragA);
trans.commit();
//getSupportFragmentManager().executePendingTransactions(); //unnecessary
}
}
When referencing back to a created Fragment please do make sure to try adding the
fragmentTransaction.addToBackStack(null);
method right before committing so that your Fragment is resumed instead of destroyed as mentioned in the developer guides.
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.
You can find this at the end of this page.