Better approach for fragment transaction - android

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

Related

Attach/detach vs replace fragment

In the following piece of code, what's the point of using detach/attach fragments instead of just replacing them?
private void showFragment(String tag) {
String oldTag = mSelectedTag;
mSelectedTag = tag;
final FragmentManager fm = getSupportFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
final Fragment oldFragment = fm.findFragmentByTag(oldTag);
final Fragment fragment = fm.findFragmentByTag(tag);
if (oldFragment != null && !tag.equals(oldTag)) {
ft.detach(oldFragment);
}
if (fragment == null) {
ft.replace(R.id.container, getContentFragment(tag), tag);
} else {
if (fragment.isDetached()) {
ft.attach(fragment);
}
}
ft.commit();
}
Why can't I just write something like this?
private void showFragment(String tag) {
final FragmentManager fm = getSupportFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container, getContentFragment(tag), tag);
ft.addToBackStack(null);
ft.commit();
}
getContentFragment method, just in case:
private Fragment getContentFragment(String tag) {
Fragment fragment = null;
if (Frag1.TAG.equals(tag)) {
fragment = new Frag1();
} else if (Frag2.TAG.equals(tag)) {
fragment = new Frag2();
}
return fragment;
}
Here's the documentation for FragmentTransaction.detach() (emphasis added):
Detach the given fragment from the UI. This is the same state as when it is put on the back stack: the fragment is removed from the UI, however its state is still being actively managed by the fragment manager. When going into this state its view hierarchy is destroyed.
So a detached fragment is still "alive" inside the FragmentManager; its view has been destroyed but all of its logical state is preserved. So when you call attach(), you are getting the same fragment back.
FragmentTransaction.replace(), passing a new fragment, however, will cause you to wind up using two different instances of the same fragment class, rather than re-using a single instance.
Personally, I've never had a need to use detach() and attach(), and have always used replace(). But that doesn't mean that there isn't a place and time where they're going to be useful.

Add only one instance of fragment into backstack

I have implemented fragments in my application. Here my code for swiching fragment in fragment_container.
private void switchFragment(Fragment fragment, boolean isAddToBackStack, String tag)
{
android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
android.support.v4.app.FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container, fragment, tag);
if (isAddToBackStack)
ft.addToBackStack(tag);
setCurrentTopFragment(Integer.parseInt(tag));
ft.commit();
}
I have 4 fragments A,B,C and D and for switching between this fragmnets I am using above method. I have A,C,B in my backstack. If again I switch to fragmnet A, my backstack is like A,C,B,A. What I actually want is If I swich to A again I want backstack sequence like this C,B,A. Means Remove old instance from backstack and add new to it.
First get the back-stacked Fragment by id which you need to remove:
Fragment fragment = getSupportFragmentManager().getFragment(new Bundle(), TAG_KEY)
or there are several methods getBackStackEntryCount(), getBackStackEntryAt. After getting the fragment which you need to remove. Remove it from the fragment back-stack.
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction trans = manager.beginTransaction();
trans.remove(fragment);
Then you can add a new fragment Done :)

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.

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.

hide existing fragment and load new one on same frame

I use the one frame on which changing the fragment views, the problem I have is when I place add instead of replace the new view is placed on the existing one
all I need is it has to replace the existing and place the new one
As I have to reuse the current view when back button is pressed cannot use the replace
Fragment fragment = null;
fragment = new DetailFragment2();
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.frame_container2, fragment)
.addToBackStack(null).commit();
If you don't use the support library (which is your actual case), use this code:
final FragmentTransaction ft =
getFragmentManager().beginTransaction();
ft.replace(R.id.frame_container2, new DetailFragment2());
ft.commit();
If one day you'll decide to use the support library, then use this instead (it's only a matter of replacing getFragmentManager() with getSupportFragmentManager()):
final FragmentTransaction ft =
getSupportFragmentManager().beginTransaction();
ft.replace(R.id.frame_container2, new DetailFragment2());
ft.commit();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction;
transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.frame_container2,fragment.newIstance());
transaction.addToBackStack(null);
transaction.commit();
And you need to create newIstance in Fragment class:
public static fragment newIstance(){
fragment frag= new fragment();
return frag;
}

Categories

Resources