Pierre Rymiortz
Pierre Rymiortz

Reputation: 1127

Fragment disappears from backstack

Why does FragmentTwo disappear from the backstack in the following situation:

Before I override onKeyDown() and start implementing my own backstack for Fragments I wanted to ask if there is something obvious I am missing? Note there is no configuration change happening when testing this.

enter link description here

Details:

FragmentOne's button click handler contains:

    FragmentTransaction ft = getFragmentManager().beginTransaction();
    FragmentTwo fragment = new FragmentTwo();
    ft.addToBackStack(null);
    ft.replace(android.R.id.content, fragment).commit();

FragmentTwo button click is handled in the Activity:

    getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    FragmentFour fragmentThree = new FragmentThree();
    FragmentFive fragmentFive = new FragmentFive();

    ActionBar.Tab tab = getActionBar().newTab().setText("Frag 3").setTabListener(new CustomTabListener<FragmentThree>(fragmentThree));
    getActionBar().addTab(tab);
    tab = getActionBar().newTab().setText("Frag 4").setTabListener(new CustomTabListener<FragmentFour>(fragmentFour));
    getActionBar().addTab(tab);

where the tab listener is:

public static class CustomTabListener<T extends Fragment> implements TabListener {
    Fragment fragment;

    public CustomTabListener(Fragment fragment) {
        this.fragment = fragment;
    }

    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        ft.replace(android.R.id.content, fragment);
    }

    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        ft.remove(fragment);
    }

    public void onTabReselected(Tab tab, FragmentTransaction ft) {

    }
}

I if add more Fragments to backstack before FragmentThree is shown, it is always and only the Fragment immediately before FragmentThree that has disappeared.

When I leave the Tabbed user view by pressing the back key and return to FragmentOne, the tabs are still showing. I understand I need to reset the ActionBar to NAVIGATION_MODE_STANDARD, but it's not clear why FragmentOne is showing instead of FragmentTwo.

Upvotes: 4

Views: 4855

Answers (5)

lakshman
lakshman

Reputation: 569

@Pierre Rymiortz, i just did the workaround for the Issue. please refer the Code below.

public class StartActivity extends SherlockFragmentActivity implements
    TabListener, OnBackStackChangedListener {

private Context mContext = null;

private ActionBar mActionBar;

private Tab mTab;

private Fragment mSelectFragment;

private final static int ONE = 0;
private final static int TWO = 1;
private final static int THREE = 2;
private final static int PLAYERS = 3;

private MenuItem mPlayerItem = null;

private FragmentTransaction mFragmentTransaction = null;

/**
 * by default is false - TABS were taken in the starting true - when we are
 * changing back to TABS from STANDARD
 */
private boolean mTabsChanges = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    mContext = this;

    mActionBar = getSupportActionBar();

    // hiding the ActionBar icon
    mActionBar.setDisplayShowHomeEnabled(true);

    // for displaying the Arrow button
    mActionBar.setDisplayHomeAsUpEnabled(true);

    // hide Actionbar title
    mActionBar.setDisplayShowTitleEnabled(true);

    // Create Actionbar Tabs
    mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    // Create first Tab
    mTab = mActionBar.newTab().setTabListener(this);
    mTab.setText("One");
    mActionBar.addTab(mTab);

    // Create Second Tab

    mTab = mActionBar.newTab().setTabListener(this);
    mTab.setText("Two");
    mActionBar.addTab(mTab);

    // Create Three Tab
    mTab = mActionBar.newTab().setTabListener(this);
    mTab.setText("Three");
    mActionBar.addTab(mTab);

    getSupportFragmentManager().addOnBackStackChangedListener(this);

}

@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
    mFragmentTransaction = ft;
    if (mTabsChanges) {
        // nothing to do here
        mTabsChanges = false;
    } else {
        switch (mActionBar.getSelectedNavigationIndex()) {
        case ONE:
            clearBackStack(mTabsChanges);
            mSelectFragment = new Fragment1();
            ft.replace(R.id.fragment_container, mSelectFragment, ONE + "");
            break;
        case TWO:
            clearBackStack(mTabsChanges);
            mSelectFragment = new Fragment2();
            ft.replace(R.id.fragment_container, mSelectFragment, TWO + "");
            break;
        case THREE:
            clearBackStack(mTabsChanges);
            mSelectFragment = new Fragment3();
            ft.replace(R.id.fragment_container, mSelectFragment, THREE + "");
            break;
        }
    }

}

@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    Toast.makeText(
            mContext,
            "tab unselected." + mActionBar.getSelectedNavigationIndex()
                    + "", Toast.LENGTH_SHORT).show();
}

@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
    Toast.makeText(
            mContext,
            "tab reselected." + mActionBar.getSelectedNavigationIndex()
                    + "", Toast.LENGTH_SHORT).show();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // TODO Auto-generated method stub
    MenuInflater mInflate = getSupportMenuInflater();
    mInflate.inflate(R.menu.header_menu, menu);

    mPlayerItem = menu.findItem(R.id.men_set_players);

    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == R.id.men_set_players) {
        addFragmentToStack();
        return true;
    }
    return false;
}

private void addFragmentToStack() {

    // Add the fragment to the activity, pushing this transaction
    // on to the back stack.
    mActionBar.setTitle(getResources().getString(R.string.menu_players));
    mSelectFragment = new PlayersFragment();
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.replace(R.id.fragment_container, mSelectFragment, "Players");
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    ft.addToBackStack("players");
    ft.commit();
}

@Override
public void onBackStackChanged() {
    FragmentManager fm = getSupportFragmentManager();
    if (fm.getBackStackEntryCount() > 0) {
        int TOP = fm.getBackStackEntryCount() - 1;
        Log.i("BACKSTACK", TOP + ".."
                + fm.getBackStackEntryAt(TOP).getName());
        if (fm.getBackStackEntryAt(TOP).getName()
                .equalsIgnoreCase("players")) {
            if (mActionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS) {
                mTabsChanges = false;
                mActionBar
                        .setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
            }
        } else {
            if (mActionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_STANDARD) {
                mTabsChanges = true;
                mActionBar
                        .setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
            }
        }

    } else {
        if (mActionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_STANDARD) {
            mTabsChanges = true;
            mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        } else if (mActionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS) {
            mTabsChanges = false;
        }
    }

    Toast.makeText(mContext,
            "FM Back count." + fm.getBackStackEntryCount(),
            Toast.LENGTH_SHORT).show();
}

public void clearBackStack(boolean callFromSelection) {
    if (!callFromSelection) {
        FragmentManager fm = getSupportFragmentManager();
        for (int i = 0; i < fm.getBackStackEntryCount(); i++) {
            fm.popBackStackImmediate();
        }
    } else {
        Toast.makeText(mContext, "callfromreapprearence",
                Toast.LENGTH_SHORT).show();
    }
}

}

Upvotes: 2

Matt
Matt

Reputation: 690

According to the code you've shown above, you never add Fragment2 to the back stack.

@gabe, @Pierre, The reason you are getting an IllegalStateException by using addToBackStack() in the listener is documented in the API http://developer.android.com/reference/android/app/ActionBar.TabListener.html

public abstract void onTabSelected (ActionBar.Tab tab, FragmentTransaction ft)  

Added in API level 11

Called when a tab enters the selected state.

Parameters tab The tab that was selected

ft A FragmentTransaction for queuing fragment operations to execute during a tab switch. The previous tab's unselect and this tab's select will be executed in a single transaction.

This FragmentTransaction does not support being added to the back stack.

The easy way around this is to get your own FragmentTransaction inside the TabListener by using the FragmentManager and add it to the back stack. Something like this

    public static class CustomTabListener<T extends Fragment> implements TabListener {
    Fragment fragment;

    public CustomTabListener(Fragment fragment) {
        this.fragment = fragment;
    }

    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        FragmentManager fm = getFragmentManager();
        FragmentTransaction newFragTrans = fm.beginTransaction();
        newFragTrans.replace(android.R.id.content, fragment);
        newFragTrans.addToBackStack(null);
        newFragTrans.commit()
        
    }

    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        ft.remove(fragment);
    }

    public void onTabReselected(Tab tab, FragmentTransaction ft) {

    }
}

I only modified onTabSelected, but you get the point. Also, don't forget to commit() this transaction because the listener will only automatically commit() the supplied FragmentTransaction ft

One last thing, ensure you are calling addToBackStack() after performing your transaction, because that's what you are adding to the stack, rather than the actual fragment. In your code above this was not done for Fragment1's button click.

Hope this helps.

Upvotes: 1

draksia
draksia

Reputation: 2371

I think you need to remove the ft.remove in the onTabUnselected() function since replace already does this.

Upvotes: 1

Leonidos
Leonidos

Reputation: 10518

Always use this code-block to replace fragment:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(android.R.id.content, YOUR_FRAGMENT);
ft.addToBackStack(null);
ft.commit();

Upvotes: 1

n3utrino
n3utrino

Reputation: 2381

I think the problem is if you build your tabs

public void onTabSelected(Tab tab, FragmentTransaction ft) {
    ft.replace(android.R.id.content, fragment);
}

gets called and the fragment before is not added to the backstack

did you try to call

ft.addToBackStack(null);

in the Fragment Two button handling code.

But you need to implement onBackPressed() anyway to get rid of the tabs. I think I would make new activity with tabs.

Upvotes: 2

Related Questions