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.
Related
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
}
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.
Which is the better approach for fragment transaction in terms of optimization and performance?
1.
ParentFragment-
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container, ChildFragment.newInstance(arrayList));
ft.commit();
ChildFragment-
public static ChildFragment newInstance(ArrayList<PlanModel> bPlanModels) {
ChildFragment fragment = new ChildFragment();
Bundle bundlearrayList = new Bundle();
bundlearrayList.putSerializable(AppConstant.ARRAYlIST, bPlanModels);
fragment.setArguments(bundlearrayList);
return fragment;
}
2.
ParentFragment-
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container, ChildFragment.newInstance(arrayList));
ft.commit();
ChildFragment-
private ArrayList<BrowsePlanModel> bPlanModels;
public ChildFragment(ArrayList<PlanModel> bPlanModels)
{
this.bPlanModels=bPlanModels
}
public static ChildFragment newInstance(ArrayList<PlanModel> bPlanModels)
{
ChildFragment fragment = new ChildFragment(bPlanModels);
return fragment;
}
3.
ParentFragment-
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();.
ChildFragment fragment = new ChildFragment();
Bundle bundlearrayList = new Bundle();
bundlearrayList.putSerializable(AppConstant.ARRAYlIST, bPlanModels);
fragment.setArguments(bundlearrayList);
ft.replace(R.id.container, fragment);
ft.commit();
All approaches are incorrect.
In the first one array is stored to bundle as serializable which leads to lack in performance. You should change it to parcelable array.
bundlearrayList.putParcelableArrayList(AppConstant.ARRAYlIST, bPlanModels);
In this case your list objects have to implement Parcelable interface.
Of course the first one could be right, if you put parcelable array instead of serializable, also use builder for more readability:
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, ChildFragment.newInstance(arrayList))
.commit();
Second one is completely wrong, because default constructor should be left in the Fragment, otherwise system won't be able to restore its state and throws an exception on attempt. Also if you'll add it, it will be also wrong, because initial arguments won't be restored because them don't present in input bundle.
Third one is bad encapsulated if you tried to fill bundle in the beginning fragment, because filling a bungle is an inner realization.
By the way, in real projects i prefer to encapsulate responsibility of transactions from beginning fragment to a particular TransactionManager class. The approach is easy maintained with di frameworks, like dagger2
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);
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();