6

I have successfully set up tabs in the Action Bar using the following example from Google themselves at http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabs.html as a bases. My code looks like so:

public class Main extends SherlockFragmentActivity {

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);        

    getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    ActionBar.Tab tab1 = getSupportActionBar().newTab();
    tab1.setIcon(R.drawable.ic_tab_example_selected);
    tab1.setTabListener(new TabListener<Tab1>(this, "A", Tab1.class));
    getSupportActionBar().addTab(tab1);

    ActionBar.Tab tab2 = getSupportActionBar().newTab();
    tab2.setIcon(R.drawable.ic_tab_example_selected);
    tab2.setTabListener(new TabListener<Tab2>(this, "B", Tab2.class));
    getSupportActionBar().addTab(tab2);

    ActionBar.Tab tab3 = getSupportActionBar().newTab();
    tab3.setIcon(R.drawable.ic_tab_example_selected);
    tab3.setTabListener(new TabListener<Tab3>(this, "C", Tab3.class));
    getSupportActionBar().addTab(tab3);

    ActionBar.Tab tab4 = getSupportActionBar().newTab();
    tab4.setIcon(R.drawable.ic_tab_example_selected);
    tab4.setTabListener(new TabListener<Tab4>(this, "D", Tab4.class));
    getSupportActionBar().addTab(tab4);     
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getSupportMenuInflater();
    inflater.inflate(R.menu.menu, menu);
    return true;
}

public class TabListener<T extends Fragment> implements ActionBar.TabListener {
    private Fragment mFragment;
    private final SherlockFragmentActivity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    /** Constructor used each time a new tab is created.
      * @param activity  The host Activity, used to instantiate the fragment
      * @param tag  The identifier tag for the fragment
      * @param clz  The fragment's Class, used to instantiate the fragment
      */
    public TabListener(SherlockFragmentActivity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }       

    /* The following are each of the ActionBar.TabListener callbacks */

    public void onTabSelected(Tab tab, FragmentTransaction ft) {

        // Check if the fragment is already initialized
        if (mFragment == null) {
            // If not, instantiate and add it to the activity
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content, mFragment, mTag);
        } else {
            // If it exists, simply attach it in order to show it
            ft.attach(mFragment);
        }
    }

    public void onTabUnselected(Tab tab, FragmentTransaction ft) {

        if (mFragment != null) {
            // Detach the fragment, because another one is being attached
            ft.detach(mFragment);
        }
    }

    public void onTabReselected(Tab tab, FragmentTransaction ft) {
        // User selected the already selected tab. Usually do nothing.
    }
}
}

With this I have multiple tabs that switch between the different fragments in each tab. However, my problem starts when in a tab and changing fragments from within a tab. This is the problem:

When I am in Tab 1, I swap the initial fragment loaded in the tab with a new fragment. I then go to Tab 2 which shows it's initial fragment. However, the view of the fragment swapped to in Tab 1 still shows behind Tab 2 fragment:

Tab 1, Fragment 2 Tab2, Fragment 1

This is code I am currently using to change the fragment from within Tab 1:

// Create new fragment and transaction
    FragmentTransaction transaction = ctx.getFragmentManager().beginTransaction();
    transaction.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right);

    // Replace whatever is in the fragment_container view with this fragment,
    // and add the transaction to the back stack
    transaction.replace(container, fragment, tag);

    if(addToBackStack)
        transaction.addToBackStack(tag);

    // Commit the transaction
    transaction.commit();

All this is being accomplished via ActionBar Sherlock and Google v4 support library.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Richard Lewin
  • 1,870
  • 1
  • 19
  • 26
  • i am trying to implment Fragments based Tabs as you have but i am not able to get it working. i have posted my question here. Please can you help me figure out what i am doing wrong http://stackoverflow.com/questions/13761581/android-tabhost-inside-fragmentactivity – Harsha M V Dec 07 '12 at 12:42

1 Answers1

7

Ok, so this answer assumes you want to wipe each tabs back history every time you swap tabs. What I mean by that is Tab 1 starts on frag 1, then you click and change it to frag 2. If you select Tab 2, you will be undoing the history of Tab 1 and next time you click Tab 1 you will be back to frag 1.

With that said here is the solution: Replace your onTabUnselected with the below

public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        if (mFragment != null) {
            //this segment removes the back history of everything in the tab you are leaving so when you click on the tab again you go back to a fresh start
            FragmentManager man = mActivity.getFragmentManager();
            if(man.getBackStackEntryCount()>0) //this check is required to prevent null point exceptions when clicking off of a tab with no history
                man.popBackStack(man.getBackStackEntryAt(0).getName(), FragmentManager.POP_BACK_STACK_INCLUSIVE); //this pops the stack back to index 0 so you can then detach and then later attach your initial fragment
            //also it should be noted that if you do popbackstackimmediate here instead of just popbackstack you will see a flash as the gui changes back to the first fragment when the code executes
            //end
            ft.detach(mFragment);
        }
    }
jp36
  • 1,873
  • 18
  • 28
  • 1
    Thanks for the answer. I have tried it out and it clears out the old view now just perfectly. jp36, would you have any idea as how to keep the tabs history if required? – Richard Lewin Apr 19 '12 at 08:18
  • If I were required to keep the tabs history, I would probably abandon attempts to use the built in BackStack and not make the calls to `.addToBackStack(tag)`. Instead I would create my own 'backstacks' for each tab, and I would intercept the back button presses and manually pop things off of my self managed stacks and use that to change the current fragment. – jp36 Apr 20 '12 at 15:00
  • 1
    A word of caution though, `replace` without using the built in backstack and backstackpops like I suggested in the answer will cause you to run into problems when switching tabs. Because the tabs just `attach` and `detach` tabs while `replace` removes the fragment that the tab will be trying to attach. You'd need to investigate either using `detach(previous); attach(popped);` (which might lead to memory issues, I'm not sure) or you'd need to do some other work in the tab changing code to point to the current fragment instead of the top level fragment – jp36 Apr 20 '12 at 15:08
  • I found a really good solution which keeps the backstack intact as well: http://stackoverflow.com/questions/10502786/switching-fragments-within-tab - that way you'd be able to switch between tabs and still keep your inner-tab history intact (important for drill-down lists). – AgentKnopf Feb 17 '13 at 14:55