I've been working with fragments for a while now, but I regularly encounter a problem that just annoys me. Fragments remain drawn over each other some times. Now, I managed to isolate one use case for this, and it goes like this:
Add Fragment A (also use addToBackStack with a name "backstack_state")
Replace Fragment A with Fragment B (use addToBackStack)
Replace Fragment B with Fragment C WITHOUT using addToBackStack
at a given point use popBackStack("backstack_state", 0) and here comes the issue:
The backstack is popped until Fragment A but Fragment C is overlaid with Fragment A, both are visible at the same time. Is this normal behavior or is it me who makes a mistake?
Here's a remark also: all the fragments have transparent background.
Thanks!
This happens because the top fragment (in this case Fragment C) is not removed. You have to remove it first inside a fragment transaction. Try this:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment topFragment = fragmentManager.findFragmentById(R.id.fragment_container);
if (topFragment != null) {
fragmentTransaction.remove(topFragment);
}
fragmentTransaction.commit();
fragmentManager.popBackStack("backstack_state", 0);
Related
I have a problem with my fragments. I use this code to navigate between the fragments:
Between the "main" fragments (without backstack, because I want the user to exit when he presses back (it works)):
FragmentManager fragmentManager = getFragmentManager();
final FragmentTransaction ft = fragmentManager.beginTransaction();
ft.setCustomAnimations(R.anim.slide_in_up_honeycomb, R.anim.slide_out_up_honeycomb);
ft.replace(R.id.container, NewsFragment.newInstance(position + 1), NewsFragment.class.getSimpleName());
ft.commit();
and between the "inner" fragments (with backstack):
FragmentTransaction ft = ((Activity) getActivity()).getFragmentManager().beginTransaction();
Fragment nextFragment = LexikonDetailFragment
.newInstance(item);
ft.setCustomAnimations(R.anim.slide_in_up_honeycomb, R.anim.slide_out_up_honeycomb);
ft.replace(R.id.container, nextFragment);
ft.addToBackStack(LexikonDetailFragment.class.getSimpleName());
ft.commit();
But in the following case:
fragment A -> fragment A1
fragment A1 -> fragment B
Press back button (should end the app)
-> goes back to fragment A1
happens:
Image
It looks like the A1 fragment doesnt gets removed from the backstack and stays in the background. I thought one possible solution could be to set to all fragments a white background..but this wouldn't fix the problem, it would just hide it. So what could be a possible solution?
Okay, i found a solution:
I had to remove the inner fragments manualy via the command:
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
I call this command each time my "outer" fragment changes. The inner ones get removed and tada...it works like a charm :)
I have a Fragment with MapView in it. I add Fragment to container with following code:
Fragment fragment = new MyLocationFragment()
String tag= fragment.getClass().getName();
FragmentTransaction transaction = fm.beginTransaction();
transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left,
R.anim.slide_in_left, R.anim.slide_out_right);
transaction.replace(R.id.container, fragment, tag);
transaction.addToBackStack(null);
transaction.commit();
Now when user presses back, this Fragment is supposed to be removed from FragmentManager. But if execute this code :
Fragment fragment = getSupportFragmentManager().findFragmentByTag(MyLocationFragment.class.getName());
It never returns null, even if I replace other fragments in same container. What can I do to remove Fragment from FragmentManager? Am I doing something wrong here ?
When an activity is destroyed, its FragmentManagersaves out its list of fragments. When the activity is recreated, the new FragmentManager retrieves the list and recreates the listed fragments to make everything as it was before.
[Refer to book "Android Programming The Big Nerd Ranch Guide" P150]
So your should remove fragment from fragmentmanager.
Refer to this Q&A Remove old Fragment from fragment manager
Your question maybe duplicate with thisHow to get a Fragment to remove itself, i.e. its equivalent of finish()?
I have a problem with the backstack behaviour. That is what I am doing:
add(fragment1) + addToBackStack(null)
replace(fragment2) + addToBackStack(null)
What is happening:
Fragment 1 is added and in the backstack
Then the second fragment replaces the first one and it is added to the backstack.
Now I want to change my last backstacked fragment with a new transaction which put a new backstack fragment so:
[frag1, frag2] becomes [frag1, frag3]
but this transaction made by a popBackStack + replace is making the frag1 to load by calling its onCreateView and onActivityCreated. I know this is the expected behaviour since this is how backstack works, but I am trying to find a way to avoid this preload.
Edit
In this question I am using the concept of backstack fragment for the transaction to be more clear. Every transaction here is an add+remove (which is a replace).
The code for replace I am using is:
public int replaceFragment(BaseFragment newFragment, boolean addToBackStack, boolean animated, PopStackMode popMode) {
if (popMode != null) {
getSupportFragmentManager().popBackStack(newFragment.getFragmentTag(), popMode == PopStackMode.POP_INCLUSIVE ? FragmentManager
.POP_BACK_STACK_INCLUSIVE : 0);
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (animated) {
ft.setCustomAnimations(R.anim.slide_in_left, 0, R.anim.slide_out_right, 0);
}
ft.replace(R.id.fragment_container, newFragment, newFragment.getFragmentTag());
if (addToBackStack) {
ft.addToBackStack(newFragment.getFragmentTag());
}
return ft.commit();
}
You can see I am creating a navigation history based on the fragment backstack, as it was kind of a browser. When a "page" is added there is a fragment and a backstack transaction. In this context, I trying to:
Remove the current fragment.
Remove the transaction from the backstack.
Add a new fragment without poping and loading the previous backstack fragment.
I hope it is more clear.
Edit 2
I have filled a request feature for a flag that supports this behavior. Find it here.
First, you should understand that the backstack doesn't save fragments, but it saves transactions instead. When you call popBackStack what it actually does is revert the previous transaction. More on this here.
I think that you can do this:
Name your transactions by providing a unique name to your addToBackStack instead of null. i.e. addToBackStack("frag1").
Don't call popBackStack + replace, but instead just call replace.
Then, in your activity, override your onBackPressed and if the current fragment being displayed is Frag3 (you can check this using findFragmentByTag if you provided a tag in the replace method) you can call getSupportFragmentManager().popBackStackImmediate("frag1", FragmentManager.POP_BACK_STACK_INCLUSIVE); (otherwise call the super.onBackPressed)
Working with fragments I've always used replace() for my transactions, but I wish I didn't have to save instance states anymore to restore a fragment's view and prevent reloading when coming back to that fragment. So, I've decided to work with add(). The thing is when I add another fragment, the previous fragment view remains in the background and that's fine (that's the behavior I expected), but the problem is I can actually interact with the views in the background. Example:
Fragment A has a Button
Fragment B has a TextView
When I add Fragment A and later add Fragment B, I'm able to click on Fragment A's Button, even staying on Fragment B's view.
I'm using:
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction().
add(getRootViewContainer(),fragment,fragment.getClass().getSimpleName());
if (shouldGoBack)
fragmentTransaction.addToBackStack(fragment.getClass().getSimpleName());
where getRootViewContainer() returns the id of the FrameLayout I'm using as my activity main container.
Now, is it really the default behavior of add()?
If so, is there a proper way to avoid this or one just has to use replace()?
What you can do here is just hide previous fragment at the time of transaction of current fragment.
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
Fragment newFragment= new MyFragment ();
ft.hide(CurrentFragment.this);
ft.show(newFragment);
ft.commit();
It worked for me just try it.
FragmentTransaction.hide(fragmentBehind); //works for me!
example :
//I have it globally available
FragmentTransaction trans = MainActivity.getManager().beginTransaction();
//not globally
FragmentTransaction trans = getFragmentManager().beginTransaction();
MapFragment newFragment = new newFragment();
trans.add(R.id.fragmentContainer, newFragment, tag);
trans.hide(this);
trans.addToBackStack(tag);
trans.commit();
Yes, this is a default behaviour of add().
If you really don't want to user replace(), you can try to disable views which are inside "old" fragment.
I'm currently using a ViewGroup (SwipeyTab implementation) to switch between Fragments. However, some Fragment "pages" get replaced by other Fragments on the same Tab, so I initially tried:
FragmentTransaction ft = fragment.getFragmentManager().beginTransaction();
ft.remove(currentFragment);
ft.add(newFragment,"");
ft.commit();
That code would remove the current fragment but not add newFragment (from Logcat, it would get instantiated but not appear).
I ended up adding it in the FragmentPagerAdapter.getItem(int position) call based on current state (based on this: Replace Fragment inside a ViewPager). However, I'd like to be able to add each newly replaced fragment to be part of the back stack.
I tried adding to backstack before removing the fragment:
currentFragment.getFragmentManager().beginTransaction().addToBackStack(null).commit();
FragmentTransaction ft = fragment.getFragmentManager().beginTransaction();
ft.remove(currentFragment);
ft.commit();
and that didn't work - it added the last fragment to the backstack, so when I pressed back, it would just reload the current fragment.
Is there anyway I can add a fragment to the backstack that has been replaced in the "non traditional" way?