Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tab content stays visible after changing tab after orientation change

I'm using ActionBarSherlock to implement an ActionBar with tabs in my application. When I start the app, and switch from tab to tab, everything is fine. However, when I change from portrait to landscape mode, the content of the tab that was last active stays visible. Changing to another tab results in the drawing of the new content on top of the old content (see image).

enter image description here

My main class:

public class TreinVerkeer extends SherlockFragmentActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setupTabs(savedInstanceState);

    }

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

    private void setupTabs(Bundle savedInstanceState) {
        ActionBar actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        Tab tab = actionBar.newTab().setText("STATIONS").setTabListener(new TabListener<StationsFragment>(this, "stations", StationsFragment.class));
        actionBar.addTab(tab);

        tab = actionBar.newTab().setText("ROUTE").setTabListener(new TabListener<RouteFragment>(this, "route", RouteFragment.class));
        actionBar.addTab(tab);

        tab = actionBar.newTab().setText("DELAYS").setTabListener(new TabListener<DelaysFragment>(this, "delays", DelaysFragment.class));
        actionBar.addTab(tab);

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

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

The TabListener (from "Adding Navigations Tabs" on the Android developer site with some minor changes):

public class TabListener<T extends SherlockFragment> implements com.actionbarsherlock.app.ActionBar.TabListener {
    private SherlockFragment mFragment;
    private final Activity 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(Activity 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 = (SherlockFragment) SherlockFragment.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.
    }
}

And StationsFragment (RouteFragment and DelaysFragment are the same, with only different text)

public class StationsFragment extends SherlockFragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.stationsfragment, container, false);
    }
}

With that the layout file for StationsFragment:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Stations" />

</LinearLayout>

And finally the Manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.myapp"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".TreinVerkeer"
            android:label="@string/app_name"
            android:theme="@style/Theme.Sherlock.Light" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

I had the same problem before right away when starting the app (so without even changing the orientation), this was solved by removing setContentView(R.layout.main) in the onCreate of the main class. I can't find a solution for this however. Can anyone help me with this?

like image 230
nhaarman Avatar asked May 08 '12 07:05

nhaarman


3 Answers

FragmentManager will automatically restore whatever fragment (and history) was currently displayed upon a configuration change. Call findFragmentByTag to see if an instance of the target fragment already exists before creating and attaching a new instance.

like image 142
Jake Wharton Avatar answered Nov 06 '22 19:11

Jake Wharton


Thanks to Jake, I've updated the onTabSelected method like this:

public void onTabSelected(Tab tab, FragmentTransaction ft) {
    SherlockFragment preInitializedFragment = (SherlockFragment) mActivity.getSupportFragmentManager().findFragmentByTag(mTag);

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

This answer is for clarification purposes only, credits go to Jake :)

like image 15
nhaarman Avatar answered Nov 06 '22 19:11

nhaarman


As well as the changes Niek posted, there are a couple of trivial changes you need to make. Mainly just changing Activity to SherlockFragmentActivity.

For the benefit of others, here's my final version which seems to work properly.

   public static class TabListener<T extends SherlockFragment> implements ActionBar.TabListener
   {
      private SherlockFragment 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(Activity activity, String tag, Class<T> clz)
      {
         mActivity = (SherlockFragmentActivity) activity;
         mTag = tag;
         mClass = clz;
      }


      public void onTabSelected(Tab tab, FragmentTransaction ft)
      {
         // Check if the fragment has already been initialised
         SherlockFragment  preInitializedFragment = (SherlockFragment) mActivity.getSupportFragmentManager().findFragmentByTag(mTag);
         if (mFragment != null)
         {
            // If it exists, simply attach it in order to show it
            ft.attach(mFragment);
         }
         else if (preInitializedFragment != null)
         {
            mFragment = preInitializedFragment;
            ft.attach(mFragment);
         }
         else
         {
            // Not found, so instantiate and add it to the activity
            mFragment = (SherlockFragment) SherlockFragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content, mFragment, mTag);
         }
      }


      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.
      }
   }
like image 5
Paul LeBeau Avatar answered Nov 06 '22 19:11

Paul LeBeau