I making my fragment changer helper class and i have some issue with it.
I call it FragmentChanger
It has a fragmentContainer
, which is a ViewGroup
, that holds all the fragments i would like to show.
I have made my own replace(Fragment fragmentToChange, boolean needSaveToBackStack)
function which is:
- Removing the old fragment fom the fragmentContainer
- Adding a new fragment to the fragmentContainer
- Optionally saves to backStack, wether
needSaveToBackStack
istrue
orfalse
.
The error is the follwowing:
If i use my replace function with saving to backStack it is works properly, i can use my device's back button to stepping back and back to previsiously added fragments it is works like a charm.
But when i would like to replace a fragment withOUT saving to backStack, there is something wrong in my code because when i stepping back, i can see on the screen the fragment that i NOT added to the backStack, and ALSO i can see an other previsiously added fragment at the same time!
So i can see 2 fragments at the very same time, like this:
This is my code:
//It is my helper class to handle replacing fragments.
public class FragmentChanger {
// fragmentTransaction
FragmentTransaction fragmentTransaction;
// the container for fragments
ViewGroup fragmentContainer;
// activity ofc
Activity act;
// Ctr: adding a default fragment to be the first so we can see it at app
// start
public FragmentChanger(Activity act, ViewGroup container, Fragment startingFragment) {
this.act = act;
this.fragmentContainer = container;
fragmentTransaction = act.getFragmentManager().beginTransaction();
fragmentTransaction.add(container.getId(), startingFragment, startingFragment.getClass().getSimpleName());
fragmentTransaction.addToBackStack(startingFragment.getClass().getSimpleName());
fragmentTransaction.commit();
}
// Replacing a fragment with an other one
public void replace(Fragment fragmentToChange, boolean needSaveToBackStack) {
fragmentTransaction = act.getFragmentManager().beginTransaction();
// replacing old fragment to the new one!
fragmentTransaction.replace(fragmentContainer.getId(), fragmentToChange, fragmentToChange.getClass().getSimpleName());
// Some null checking, and if the new fragment is NOT equals the current
// fragment
if (getCurrentFragment() != null && getCurrentFragment() != fragmentToChange) {
/*** Important** because something here is wrong ***/
// only addToBackStack when i want it, when needSaveToBackStack =
// true!
if (needSaveToBackStack) {
fragmentTransaction.addToBackStack(fragmentToChange.getClass().getSimpleName());
}
}
// commiting changes
fragmentTransaction.commit();
}
// getting current Fragment
private Fragment getCurrentFragment() {
try {
FragmentManager fragmentManager = act.getFragmentManager();
String fragmentTag = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName();
Fragment currentFragment = act.getFragmentManager().findFragmentByTag(fragmentTag);
return currentFragment;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
// Logging the back stack
public void logBackStack() {
Log("Logging back stack:", "=============");
FragmentManager fragmentManager = act.getFragmentManager();
int stackSize = fragmentManager.getBackStackEntryCount();
Log("Fragments count on the stack: ", stackSize + "");
for (int i = 0; i < stackSize; i++) {
String fragmentTag = fragmentManager.getBackStackEntryAt(i).getName();
Log("Fragment on the stack: ", fragmentTag);
}
}
private void Log(String str, String msg) {
Log.i(str, msg);
}
}
And this is my MainActivity where i test my fragment helper class:
public class MainActivity extends Activity implements OnClickListener {
// My 3 Fragment Classes, could be N other type,
// in this example I only got 3
FragmentA fragmentA;
FragmentB fragmentB;
FragmentC fragmentC;
// Button to add the fragments manually
Button addA, addB, addC;
// This is my activity's container, its a simple viewGroup
// could be anything that can hold fragments
ViewGroup fragmentContainer;
// This is my fragment changer helper class that need some revision by you
// guys
FragmentChanger fragmentChanger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//defining my adding buttons
addA = (Button)findViewById(R.id.addAbtn);
addB = (Button)findViewById(R.id.addBbtn);
addC = (Button)findViewById(R.id.addCbtn);
//setting onclicklistenrs
addA.setOnClickListener(this);
addB.setOnClickListener(this);
addC.setOnClickListener(this);
// defining my main container, this will holds fragments
fragmentContainer = (ViewGroup) findViewById(R.id.fragmentContainer);
// defining my fragments, each of them got an Activity (this), a
// Container (mainContainer), and a Layout ofc.
fragmentA = new FragmentA(this, fragmentContainer, R.layout.fragment_a_layout);
fragmentB = new FragmentB(this, fragmentContainer, R.layout.fragment_b_layout);
fragmentC = new FragmentC(this, fragmentContainer, R.layout.fragment_c_layout);
// defining my fragment changer with an activity(this), a
// container(mainContent) and a starting fragment to show!
fragmentChanger = new FragmentChanger(this, fragmentContainer, fragmentA);
}
@Override
public void onClick(View view) {
if (view.equals(addA)) {
//When adding fragmentA, i always want to save it to backstack
fragmentChanger.replace(fragmentA, true);
} else if (view.equals(addB)) {
//I dont want to save to back stack when adding B
//So if i press back button, i dont want to see fragmentB ever again.
//(But i do see, this is the error.)
fragmentChanger.replace(fragmentB, false);
} else if (view.equals(addC)) {
//When adding fragmentC, i always want to save it to backstack
fragmentChanger.replace(fragmentC, true);
}
//After any modification on fragments, i log the backstack
fragmentChanger.logBackStack();
}
}
Ps: I can clearly see that fragmentB is never on the backStack if i logging the stack each time i replace a Fragment with my helper class. Then why is it appears if i push back button?
I greatly appreciate any advice, this is my first attempt with using fragments with my own helper class and i would like to make it greatly usable.
Firstly in the constructor to your
FragmentChanger
class you are adding all the fragments onto your backstack. since you are adding them in order they are getting added as A,B and then C. After adding them to the backstack in that order you are doing more transactions on top of it. While initializing them avoid adding them to the backstack. Only add/remove them whilst doing the actual button click.There are a lot of replacements going on in your code so i do not know how to really fix this but i can give a general overview of what you should do.
If you want to add a fragment to the backStack then do a
.replace(params ).addToBackStack(Tag)
if you dont want to add a particular fragment just skip the
addToBackStack(Tag)
line completely.Also your
getCurrentFragment()
can be changed toFragment currentFrag=getFragmentManager().findFragmentById(R.id.fragment_container);
Read this ANSWER here for a better explanation and also this ANSWER
change this line
to
So here what I found wrong with your code:
1) You are mixing together tags of
Fragment
andBackStackEntry
.So here you are getting the tag of
BackStackEntry
(notFragment
), which you adding or not infragmentTransaction.addToBackStack()
. Therefore, you are getting the wrong state and using it as current fragment.2) It is generally not a good idea to store and reuse fragments like you do in your class fields:
FragmentA fragmentA;
. It can lead to problems with state and inflation, it is better to create them on every transaction, if you don't want to solve additional puzzles.Try to replace the fragment using FragmentTransacion.replace method as shown in link here
And you can refer this example as well. This works for me perfectly.