Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fragments still added to backstack without calling addToBackStack, why?

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 is true or false.

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:

enter image description here

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.

like image 810
Adam Varhegyi Avatar asked Jan 28 '14 13:01

Adam Varhegyi


2 Answers

So here what I found wrong with your code:

1) You are mixing together tags of Fragment and BackStackEntry.

String fragmentTag = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName();

So here you are getting the tag of BackStackEntry (not Fragment), which you adding or not in fragmentTransaction.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.

like image 189
Dmide Avatar answered Oct 21 '22 22:10

Dmide


change this line

    fragmentTransaction.add(container.getId(), startingFragment, startingFragment.getClass().getSimpleName());

to

    fragmentTransaction.replace(container.getId(), startingFragment, startingFragment.getClass().getSimpleName());
like image 39
MohammedAli Avatar answered Oct 22 '22 00:10

MohammedAli