Remove old Fragment from fragment manager - android

I'm trying to learn how to use Fragments in android.
I'm trying to remove old fragment when new fragment is calling in android.

You need to find reference of existing Fragment and remove that fragment using below code. You need add/commit fragment using one tag ex. "TAG_FRAGMENT".
Fragment fragment = getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT);
if(fragment != null)
getSupportFragmentManager().beginTransaction().remove(fragment).commit();
That is it.

I had the same issue. I came up with a simple solution. Use fragment .replace instead of fragment .add. Replacing fragment doing the same thing as adding fragment and then removing it manually.
getFragmentManager().beginTransaction().replace(fragment).commit();
instead of
getFragmentManager().beginTransaction().add(fragment).commit();

I had the same issue to remove old fragments. I ended up clearing the layout that contained the fragments.
LinearLayout layout = (LinearLayout) a.findViewById(R.id.layoutDeviceList);
layout.removeAllViewsInLayout();
FragmentTransaction ft = getFragmentManager().beginTransaction();
...
I do not know if this creates leaks, but it works for me.

Probably you instance old fragment it is keeping a reference. See this interesting article Memory leaks in Android — identify, treat and avoid
If you use addToBackStack, this keeps a reference to instance fragment avoiding to Garbage Collector erase the instance. The instance remains in fragments list in fragment manager. You can see the list by
ArrayList<Fragment> fragmentList = fragmentManager.getFragments();
The next code is not the best solution (because don´t remove the old fragment instance in order to avoid memory leaks) but removes the old fragment from fragmentManger fragment list
int index = fragmentManager.getFragments().indexOf(oldFragment);
fragmentManager.getFragments().set(index, null);
You cannot remove the entry in the arrayList because apparenly FragmentManager works with index ArrayList to get fragment.
I usually use this code for working with fragmentManager
public void replaceFragment(Fragment fragment, Bundle bundle) {
if (bundle != null)
fragment.setArguments(bundle);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment oldFragment = fragmentManager.findFragmentByTag(fragment.getClass().getName());
//if oldFragment already exits in fragmentManager use it
if (oldFragment != null) {
fragment = oldFragment;
}
fragmentTransaction.replace(R.id.frame_content_main, fragment, fragment.getClass().getName());
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
fragmentTransaction.commit();
}

I had issue with fragment onBackPress. I didn't want to return to old fragments.
This solution worked for me to remove old fragments from fragment manager :
FragmentManager fm = getSupportFragmentManager();
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
You can also use an if block like this :
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}

In Kotlin
val fragList = f.supportFragmentManager.fragments
for (fragment in fragList) {
f.supportFragmentManager.beginTransaction().remove(fragment).commit()
}

I was facing the same error with few views so I set the visibility of reductant views as GONE. This method solved my issue as charm.
<View>.setVisibility(View.GONE);

Related

How to get the reference of a fragment created by FragmentTransaction.replace method

I added a fragment inside an activity by using the following code:
FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.replace(R.id.parent_fragment_container, new FolderStructureFragment());
ft.commit();
My question is how can I get the reference of the added fragment. I have searched a lot of key words related to fragment and FragmentTransaction but didn't find anything close to my requirement which I think is a very basic function and should be offered to us. Any help is much appreciated!
There are the two major possiblities:
You can remember the reference by yourself
By an Tag
By ID
For example (nearly the same for by ID):
ft.replace(R.id.fragment_container, fragment, tagOfFragment);
getSupportFragmentManager().findFragmentByTag(tagOfFragment);
try using the below code.
FolderStructureFragment folderStructureFragment = (FolderStructureFragment)getActivity().getSupportFragmentManager().findFragmentById(R.id.parent_fragment_container);
FolderStructureFragment folderStructureFragment = new FolderStructureFragment();
FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.replace(R.id.parent_fragment_container, folderStructureFragment);
ft.commit();
Here , folderStructureFragment is reference for newly added fragment.
There is an easy way to get a reference to a fragment any time you need, and that is with a TAG. Whenever you add a fragment you may give it a specific tag, and you may retrieve it anytime you want with that same TAG.
This is how you add it:
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
MyFragment myFragment = new MyFragment();
fragmentTransaction.add(R.id.fragment_container, myFragment,"myFragmentTag").commit();
Now let's say, somewhere in the code you need the reference to this fragment, you only need to call the following
MyFragment myFragment = (MyFragment)getSupportFragmentManager().findFragmentByTag("myFragmentTag");
if(myFragment != null && myFragment.isAdded()){ //you should always do this check when you retrieve fragment instances
//do whatever you need with it
}

Nested fragment and back stack

I have a fragment in which there is a nested fragment which I add in this way:
if (home == null) {
home = new MyFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.addToBackStack(MyFragment.class.getName());
transaction.add(R.id.child_fragment, home).commit();
}
When I enter another fragment and go back the child fragment from above is not there. I checked and the instance is different from null.
UPDATE: I changed the code as suggested by Ashwin S Ashok but it's still not working.
Try using these methods:
// Use this if you don't want to retain the fragment.
protected void replaceFragmentStack(int container, Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(container, fragment);
fragmentTransaction.commit();
}
// Use this if you want to add the fragments in a stack.
protected void addFragmentStack(int container, Fragment fragment, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.add(container, fragment, tag);
fragmentTransaction.addToBackStack(tag);
fragmentTransaction.commit();
}
I would suggest you to use getChildFragmentManager() when making transactions inside a fragment. And its a bug i guess.
You can check out this thread it will help you alot Android 4.2: back stack behaviour with nested fragments
Also you need to go through The Curious Techizen's blog
Here is the link for the github project sample for same mechanism
I hope this will help you.

Not understanding how setRetainInstance (or RetainInstance in Xamarin Android) works?

If you have some Fragment declared right inside your XML layout file, then looks like SetRetainInstance works expectedly.
However what if the Fragment is loaded dynamically into some container in the OnCreate callback of the main Activity? I don't see how setRetainInstance means or can do in this case.
Check out this code:
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
var f = new SomeFragment();
var ft = FragmentManager.BeginTransaction();
ft.Replace(Android.Resource.Id.Content, f);
ft.Commit();
}
You can see that everytime the OnCreate is called (such as when the screen is rotated), a new SomeFragment is created and fills the main content of the Activity.
Now even if I declare a variable (field) holding reference to the instance of fragment and create it only when it's null, something like this:
SomeFragment f;
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
if(f == null) {
f = new SomeFragment(this);
f.RetainInstance = true;
}
var ft = FragmentManager.BeginTransaction();
ft.Replace(Android.Resource.Id.Content, f);
ft.Commit();
}
This also does not work, everytime the Activity is created, the field f is still null initially (not retained). The only solution I found working here is declare the field f as static, like this:
static SomeFragment f;
With that I don't even need to use RetainInstance (or setRetainInstance in Java). In fact the requirement of loading Fragments dynamically is very popular, so at that point RetainInstance seems to be less helpful?
Or I missed something simple here to still take advantage of RetainInstance? The problem is use RetainInstance and dynamically load Fragments, if you have some solution or pattern to use here, please share with me, thanks!
Although I am not familiar to Xamarin, I can answer this in Java.
Instead of using
replace(int containerViewId, Fragment fragment)
You should use
replace(int containerViewId, Fragment fragment, String tag)
For example,
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment;
fragment = (ExampleFragment) fragmentManager.findFragmentByTag("tag");
if(fragment == null)
fragment = new ExampleFragment();
fragmentTransaction.replace(R.id.fragment_container, fragment, "tag");
fragmentTransaction.commit();
But you should only use setRetainInstance if you are running longe task at background. You may have a look at this blog post. Recreating the fragment is more suggested as it can avoid unexpected behavior (e.g. not initializing some variables).

After detach cannot findFragmentbyTag

In MainFragment at onViewCreated() I try to create nested fragment by following code ...
FragmentManager fm = getChildFragmentManager();
MainSubAFragment mainSubAFragment = MainSubAFragment.newInstance();
MainSubBFragment mainSubBFragment = MainSubBFragment.newInstance();
fm.beginTransaction()
.add(R.id.contentContainer, mainSubAFragment, MainSubAFragment.class.getName())
.add(R.id.contentContainer, mainSubBFragment, MainSubBFragment.class.getName())
.detach(mainSubBFragment)
.commit();
if (fm.executePendingTransactions()) {
showSubA();
}
After executePendingTransaction I call showSubA(), but no hope return null..
public void showSubA() {
FragmentManager fm = getChildFragmentManager();
MainSubAFragment mainSubAFragment = (MainSubAFragment)
fm.findFragmentByTag(MainSubAFragment.class.getName());
MainSubBFragment mainSubBFragment = (MainSubBFragment)
fm.findFragmentByTag(MainSubBFragment.class.getName());
fm.beginTransaction()
.attach(mainSubAFragment)
.detach(mainSubBFragment)
.commit();
}
It seem detach is a cause of problem ?? The question is .. if I call detach I cannot retreive it again # findFragmentByTag() ??
project sample: dropbox download
First Create your Fragments. It is much better to Create your Fragments in a Fragment class Instead of the Original Class name as long as you extended the Fragment class. Like this.
Fragment mainSubAFragment, mainSubBFragment;
Now we have to supply those pointers with the Fragments.
mainSubAFragment = new MainSubAFragment(); <- No need to call the New Instance which is in the Object Class.
mainSubBFragment = new MainSubBFragment();
Remember they need to extended to Fragment. Which I know you did.
Now I don't know why you have to get the ChildFragmentManager but you can just directly get the FragmentManager;
FragmentManager fm = getFragmentManger();
FragmentTransaction ft = fm.beginTransaction();
Now everything is ready, we go to the Execution part.
ft.add(R.id.contentContainer,mainSubAFragment,MainSubAFragment.class.getName());
ft.add(R.id.contentContainer,mainSubBFragment,MainSubBFragment.class.getName());
ft.commit();
NOTE: They will stack there Listeners, like a normal fragment does. So Unless declare in their XML not to stack there listeners, there onclick will be called if they are position on the same location.
After you Commit add this.
You would need to add another Fragment Transaction, why? Because the Commit() that you have made might not be finished, even though this is a line by line execution. Trust me, most of the time stack calls is fighting over one another.
To avoid this,
FragmentTransaction transaction2 = fm.beginTransaction();
fm.hide(MainSubBFragment.class.getName());
Summing things up.
Fragment mainSubAFragment, mainSubBFragment;
FragmentManager fm = getFragmentManger();
FragmentTransaction ft = fm.beginTransaction();
FragmentTransaction transaction2;
ft.add(R.id.contentContainer,mainSubAFragment,MainSubAFragment.class.getName());
ft.add(R.id.contentContainer,mainSubBFragment,MainSubBFragment.class.getName());
ft.commit();
transaction2 = fm.beginTransaction();
transaction2.hide(MainSubBFragment.class.getName());
transaction2.commit();
Make Sure that the Fragment Transactions and FragmentManagers is Global Variable. So they can be used by other Methods, instead of Creating a new Instance.

Reset/reload fragment container

How can I reset or reload a fragment container, to make it empty.
I have a master detail view and I want to reset the detail container to empty on a menu item click.This works in some cases and does not in some.
NullFragment fragment = new NullFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
fragmentTransaction.replace(R.id.item_detail_container,
fragment);
int count = fragmentManager.getBackStackEntryCount();
fragmentManager.popBackStackImmediate(count, 0);
fragmentTransaction.commit();
Usually you simply remove the fragment from it.
For example do something like
getFragmentManager().beginTransaction().remove(getFragmentManager().findFragmentById(R.id.your_container)).commit();
this will remove the fragment from the your_container holding it.
This gets the fragment currently present in your_container
getFragmentManager().findFragmentById(R.id.your_container)
and this remove the fragment
getFragmentManager().beginTransaction().remove(fragment).commit();
EDIT
Also sometimes it is useful to ensure all transactions are performed and finished, this can be done by using
getFragmentManager().executePendingTransactions();

Categories

Resources