Assigning Fragments to tabs in the ActionBar with different orientations

38,678

Solution 1

It looks like my answer can be found here: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabs.html

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.example.android.apis.app;

import com.example.android.apis.R;


import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.widget.Toast;

/**
 * This demonstrates the use of action bar tabs and how they interact
 * with other action bar features.
 */
public class FragmentTabs extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final ActionBar bar = getActionBar();
        bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);

        bar.addTab(bar.newTab()
                .setText("Simple")
                .setTabListener(new TabListener<FragmentStack.CountingFragment>(
                        this, "simple", FragmentStack.CountingFragment.class)));
        bar.addTab(bar.newTab()
                .setText("Contacts")
                .setTabListener(new TabListener<LoaderCursor.CursorLoaderListFragment>(
                        this, "contacts", LoaderCursor.CursorLoaderListFragment.class)));
        bar.addTab(bar.newTab()
                .setText("Apps")
                .setTabListener(new TabListener<LoaderCustom.AppListFragment>(
                        this, "apps", LoaderCustom.AppListFragment.class)));
        bar.addTab(bar.newTab()
                .setText("Throttle")
                .setTabListener(new TabListener<LoaderThrottle.ThrottledLoaderListFragment>(
                        this, "throttle", LoaderThrottle.ThrottledLoaderListFragment.class)));

        if (savedInstanceState != null) {
            bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
    }

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

        public TabListener(Activity activity, String tag, Class<T> clz) {
            this(activity, tag, clz, null);
        }

        public TabListener(Activity activity, String tag, Class<T> clz, Bundle args) {
            mActivity = activity;
            mTag = tag;
            mClass = clz;
            mArgs = args;

            // Check to see if we already have a fragment for this tab, probably
            // from a previously saved state.  If so, deactivate it, because our
            // initial state is that a tab isn't shown.
            mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
            if (mFragment != null && !mFragment.isDetached()) {
                FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction();
                ft.hide(mFragment);
                ft.commit();
            }
        }

        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            if (mFragment == null) {
                mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs);
                ft.add(android.R.id.content, mFragment, mTag);
            } else {
                ft.show(mFragment);
            }
        }

        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
            if (mFragment != null) {
                ft.hide(mFragment);
            }
        }

        public void onTabReselected(Tab tab, FragmentTransaction ft) {
            Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT).show();
        }
    }
}

Solution 2

The link provided by @Metallicraft is dead so here is some help for those who still stumbles upon this post. Go here for help with implementing tabs.

To see/read the original sample you can go to following folder if you've installed android development environment: android-sdk\samples\android-14\ApiDemos\src\com\example\android\apis\app\FragmentTabs.java

Also if you have problem with fragments overlapping after orientation change its properly related to savedinstancestate passed on. Here is a copy paste from a google tutorial that explains the fragment overlapping issue and how to avoid it:

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.news_articles);

        // Check that the activity is using the layout version with
        // the fragment_container FrameLayout
        if (findViewById(R.id.fragment_container) != null) {

            // However, if we're being restored from a previous state,
            // then we don't need to do anything and should return or else
            // we could end up with overlapping fragments.
            if (savedInstanceState != null) {
                return;
            }

            // Create an instance of ExampleFragment
            HeadlinesFragment firstFragment = new HeadlinesFragment();

            // In case this activity was started with special instructions from an Intent,
            // pass the Intent's extras to the fragment as arguments
            firstFragment.setArguments(getIntent().getExtras());

            // Add the fragment to the 'fragment_container' FrameLayout
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.fragment_container, firstFragment).commit();
        }
    }
}

Solution 3

Create a new Android Sample project, choose the Support4Demos sample. You will find the FragmentTabs sample there.

Solution 4

I had more or less the same problem, but the solutions presented above did not seem to work out in my situation. Eventually I found the following solution:

    @Override
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        if (mFragment == null) {
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            ft.replace(android.R.id.content, mFragment, mTag); // Use replace iso add
        }
        else {
            ft.attach(mFragment);
        }
    }
Share:
38,678

Related videos on Youtube

Metallicraft
Author by

Metallicraft

Updated on March 18, 2020

Comments

  • Metallicraft
    Metallicraft about 4 years

    I have 3 fragments and an activity. I want to enable tabs on the ActionBar and assign a Fragment to each of the 3 tabs. How do I hook that up correctly?

    ORIGINAL POST

    I have an app that I'm developing using the Google I/O app as a guide. I've implemented tabs into the ActionBar. They seem to be working until the tablet's orientation changes. For example, all 3 tabs have a Fragment. I can switch between them just fine, but when I change the orientation, whatever the Fragment I was last viewing stays visible, but clicking the tabs no longer changes the view... like they have become disconnected. As expected, going back to the original orientation does not "fix" it.

    I've looked into saving and restoring state, but I'm not seeing how those would help.

    EDIT

    module level:

    Fragment mFragmentA = (Fragment) new AFragmentTab();
    Fragment mFragmentB = (Fragment) new BFragmentTab();
    Fragment mFragmentC = (Fragment) new CFragmentTab();
    

    I have something like this in the activity's onCreate:

    ActionBar actionBar = getActionBar();
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    actionBar.setDisplayShowTitleEnabled(false);
    
    ActionBar.Tab tabA = actionBar.newTab().setText("text a");
    ActionBar.Tab tabB = actionBar.newTab().setText("text b");
    ActionBar.Tab tabC = actionBar.newTab().setText("text c");
    
    tabA.setTabListener(this);
    tabB.setTabListener(this);
    tabC.setTabListener(this);
    
    actionBar.addTab(tabA);
    actionBar.addTab(tabB);
    actionBar.addTab(tabC);
    

    and a TabListener like this:

    EDIT this is removed

    class MyTabListener implements ActionBar.TabListener {
        private Fragment mFragment;
    
        // Called to create an instance of the listener when adding a new tab
        public MyTabListener(Fragment fragment) {
            mFragment = fragment;
        }
    
        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            ft.add(R.id.fragment_content, mFragment, null);
        }
    
        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
            ft.remove(mFragment);
        }
    
        public void onTabReselected(Tab tab, FragmentTransaction ft) {
            // do nothing
        }
    }
    

    EDIT

    I've moved the TabListener. Instead of it being a separate class, I implement the TabListener on the Activity. Then on the Selected and Unselected methods I have something like:

    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        switch (tab.getPosition()) {
        case 0:
            ft.add(R.id.fragment_content, mFragmentA, null);
            break;
        case 1:
            ft.add(R.id.fragment_content, mFragmentB, null);
            break;
        case 2:
            ft.add(R.id.fragment_content, mFragmentC, null);
            break;
        }
    }
    
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        switch (tab.getPosition()) {
        case 0:
            ft.remove(mFragmentA);
            break;
        case 1:
            ft.remove(mFragmentB);
            break;
        case 2:
            ft.remove(mFragmentC);
            break;
        }
    }
    

    It's still doing the same thing. I really don't know what's happening.

    • PankajAndroid
      PankajAndroid over 10 years
      can tell me what solution you had use for this.. same problem i am facing..
  • Android
    Android almost 12 years
    Feels like you need to update your link, or like provide some solid solution here only. That would be rather more helpful. Thanks.
  • Rasmus
    Rasmus over 10 years
    This was awesome! I was somehow missing detaching the fragments in the constructor
  • AlikElzin-kilaka
    AlikElzin-kilaka over 10 years
    Detaching will close the fragment to be reloaded instead of just appear at the last state it was. Use hide instead!
  • Luis Pena
    Luis Pena about 10 years
    returning null when the savedInstanceState != null worked for me \O/
  • Zordid
    Zordid about 10 years
    This cannot really work, sorry. Explicitly because of the lines "if (mFragment != null)..." in constructor of TabListener, when orientation changes happen, the saved instance states of visible fragments are thrown away! They will be detached and reattached again - losing their savedInstanceState!
  • mikemike396
    mikemike396 over 9 years
    @AlikElzin-kilaka I agree with you. If you change the attach to "show" and the detach to "hide" it works perfect. Fragments are not recreated!