Reputation: 1127
Why does FragmentTwo
disappear from the backstack in the following situation:
Fragment
called FragmentOne
in an Activity
.FragmentOne
holds a Button
. When clicked, it launches FragmentTwo
, which is added to the Fragment
backstack. FragmentTwo
has a Button
, which when clicked adds two tabs to the ActionBar
linked to two Fragments
, FragmentThree
and FragmentFour
. FragmentThree
is visible. FragmentTwo
. Instead, I see FragmentOne
. Where did FragmentTwo
go? 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.
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
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
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
Reputation: 2371
I think you need to remove the ft.remove
in the onTabUnselected()
function since replace already does this.
Upvotes: 1
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
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