Tab content stays visible after changing tab after orientation change - android

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).
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?

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.

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 :)

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.
}
}

Related

Going back to fragment second time using tabs shows blank fragment

I have 2 tabs in my app, using tablistner and I'm facing an issue when I'm navigating in a very specific situation to other tab and then navigating back to the first tab.
It happens after i load a fragment called "setFrom" from another fragment:
public void LoadSetFrom ()
{
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
SherlockFragment setFrag = new setFrom();
ft.replace(R.id.main_layout, setFrag, "setfrom");
ft.commit();
}
This "setFrom" fragment is one of my 2 tabs, after that I'm navigatin to the second tab and when I'm going back to "setFrom" the tabs navigation still appears but the fragment is totally blank.
I'm using TabListener that way:
public class TabListener<T extends SherlockFragment> implements com.actionbarsherlock.app.ActionBar.TabListener
{
private SherlockFragment mFragment;
private setFrom fromFragment;
private final SherlockFragmentActivity mActivity;
private final String mTag;
private final Class<T> mClass;
public TabListener(SherlockFragmentActivity activity, String tag, Class<T> clz) {
mActivity = activity;
mTag = tag;
mClass = clz;
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
SherlockFragment preInitializedFragment = (SherlockFragment)mActivity.getSupportFragmentManager().findFragmentByTag(mTag);
if (preInitializedFragment == null) {
mFragment = (SherlockFragment) SherlockFragment.instantiate(mActivity, mClass.getName());
ft.add(R.id.main_layout, mFragment, mTag);
}
else {
ft.attach(preInitializedFragment);
}
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null)
ft.detach(mFragment);
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
}
After checking onTabSelected, "setFrom" is not null,it attached to the right fragment and it goes to ft.attach(preInitializedFragment) which is fine.
My question is why after the attach to the right fragment the view is still blank?
I was facing the same problem
Solved it by adding setRetainInstance(true); to my Fragment's onCreate

Sherlock tabs not working on orientation change

I managet to set up three tabs using Sherlock ActionBar. The only problem is that when orientation is changed, tabs can not be tapped any more. It seem like the onTabSelected() is not called. Example: I am in portrait and the tab2 is selected. I change into lanscape. Tab2 is still selected, I tap tab3 but nothing happens. Then when I go back to portrait again, tab3 is shown. I am testing in Android 2.3.6.
This is the main activity:
public class Activity_Main extends SherlockFragmentActivity {
ActionBar.Tab tab1, tab2, tab3;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTabs();
}
void setTabs(){
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
tab1 = actionBar.newTab();
tab2 = actionBar.newTab();
tab3 = actionBar.newTab();
tab1.setText("Week");
tab2.setText("Today");
tab3.setText("ToDo");
tab1.setTabListener(new TabListener<Fragment_Start_Week>(this, "week", Fragment_Start_Week.class));
tab2.setTabListener(new TabListener<Fragment_Start_Today>(this, "today", Fragment_Start_Today.class));
tab3.setTabListener(new TabListener<Fragment_Start_Todo>(this, "todo", Fragment_Start_Todo.class));
}
private 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) {
SherlockFragment preInitializedFragment = (SherlockFragment) ((FragmentActivity) 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;
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
// Detach the fragment, because another one is being attached
ft.detach(mFragment);
}
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
}
This is a fragment:
public class Fragment_Start_Week extends SherlockFragment implements OnClickListener{
void create_table() {
...
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup group, Bundle saved)
{
return inflater.inflate(R.layout.fragment_start_week, group, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
create_table();
}
#Override
public void onClick(View view) {
...
}
}
TIA
After a lot of trial and errors I've found the following solution for this bug:
#Override
public void onConfigurationChanged(Configuration newConfig) {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
super.onConfigurationChanged(newConfig);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
}
The trick is to change the navigation mode to list then change back to tabs.

Hiding/Showing SherlockFragments in ActionBar.TabListener Class, content is empty

I have an app built using TabBarSherlock and the Support library to add ActionBar support to pre 3.0 devices. I can't remember what tutorial I followed to create the Tabs and the Listener but I have the following code.
Firstly creating the Tabs (Inside a SherlockFragmentActivity):
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
/*--------Setup News Tab--------*/
Tab tab1 = actionBar.newTab()
.setText("News")
.setTabListener(new TabListener<TabFragment>(
this, "tab1", TabFragment.class));
Bundle newsBundle = new Bundle();
newsBundle.putInt("news_id", newsID);
tab1.setTag(newsBundle);
actionBar.addTab(tab1);
/*------------------------------*/
// This is repeated 3 more times to total 4 Tabs.
Then I have a classCalled TabListener which is used in each of these Tabs to detect when they have been selected.
public class TabListener<T extends Fragment> implements ActionBar.TabListener{
private TabFragment mFragment;
private final Activity mActivity;
private final String mTag;
private final Class<T> mClass;
public TabListener(Activity activity, String tag, Class<T> clz) {
mActivity = activity;
mTag = tag;
mClass = clz;
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// Check if the fragment is already initialised
if (mFragment == null) {
Log.v("FRAGMENT", "FRAGMENT NEEDS TO BE CREATED");
mFragment = (TabFragment) Fragment.instantiate(mActivity, mClass.getName(), (Bundle)tab.getTag());
ft.add(android.R.id.content, mFragment, mTag);
} else {
Log.v("FRAGMENT", "FRAGMENT ALREADY CREATED");
ft.show(mFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
ft.hide(mFragment);
}
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
}
With the class TabFragment containing a ViewPager for each Tab. My issue is that when selecting a Tab other than the first one the content inside the Fragment does not show. From the logs in place when the Fragment is initialised I can tell the views are being created just not being shown, it's just a blank area showing the background.
Instead of using show() and hide(), use attach() and detach(). Using show/hide does not remove the view hierarchy from the screen, simply hides it, so there might be issues related to that.
You are not detaching fragment rather you are just hiding it. so you should detach it so that other fragment should be attached in your onTabUnSelected.
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.
Example:
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;
}
}
and you onTabUnSelected should be like this
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
// Detach the fragment, because another one is being attached
ft.detach(mFragment);
}
}
Content is empty on tab bar then write below code
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(actionBar.NAVIGATION_MODE_STANDARD);

Android Actionbar Sherlock with Tabs

I am trying to implement ActionBar Sherlock with Tabs below that as shown in the above wire-frame.
Should i use TabActivity ? - since i saw that it is deprecated. Which is the best way to achieve the same.
I implemented this functionality with a SherlockFragmentActivity as tabview container and with SherlockFragment as tabs. Here is a sketch (I omitted the usual Android activity stuff):
This is the tabview activity with two tabs:
public class TabViewActivity extends SherlockFragmentActivity {
// store the active tab here
public static String ACTIVE_TAB = "activeTab";
#Override
public void onCreate(Bundle savedInstanceState) {
..
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// add the first tab which is an instance of TabFragment1
Tab tab1 = actionBar.newTab()
.setText("TabTitle1")
.setTabListener(new TabListener<TabFragment1>(
this, "tab1", TabFragment1.class));
actionBar.addTab(tab1);
// add the second tab which is an instance of TabFragment2
Tab tab2 = actionBar.newTab()
.setText("TabTitle2")
.setTabListener(new TabListener<TabFragment2>(
this, "tab2", TabFragment2.class));
actionBar.addTab(tab2);
// check if there is a saved state to select active tab
if( savedInstanceState != null ){
getSupportActionBar().setSelectedNavigationItem(
savedInstanceState.getInt(ACTIVE_TAB));
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
// save active tab
outState.putInt(ACTIVE_TAB,
getSupportActionBar().getSelectedNavigationIndex());
super.onSaveInstanceState(outState);
}
}
And this is the TabFragment that holds a tab's content:
public class TabFragment extends SherlockFragment {
// your member variables here
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_va_esh, container, false);
... // do your view initialization here
return view;
}
}
And finally this is the TabListener that handles tab switches:
public class TabListener<T extends Fragment> implements ActionBar.TabListener{
private TabFragment mFragment;
private final Activity mActivity;
private final String mTag;
private final Class<T> mClass;
public TabListener(Activity activity, String tag, Class<T> clz) {
mActivity = activity;
mTag = tag;
mClass = clz;
}
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 = (TabFragment) Fragment.instantiate(
mActivity, mClass.getName());
mFragment.setProviderId(mTag); // id for event provider
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.
}
}
I believe TabActivity is deprecated in favor of using Fragments -- not because tab navigation is a deprecated concept. Simply use Fragments and a TabWidget.
Also, here's a similar question.
Edit:
Here's an example courtesy of Google: Android Tabs the Fragment Way

Double fragment rotating Android with ActionBar

I've made a simple Android Activity with an ActionBar to switch between 2 fragments.
It's all ok until I rotate the device. In facts, when I rotate I've got 2 fragment one over the other: the previous active one and the first one.
Why?
If the rotation destroy and recreate my activity, why I obtain 2 fragments?
Sample code:
Activity
package rb.rfrag.namespace;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.Activity;
import android.os.Bundle;
public class RFragActivity extends Activity {
/** Called when the activity is first created. */
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Notice that setContentView() is not used, because we use the root
// android.R.id.content as the container for each fragment
// setup action bar for tabs
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
//actionBar.setDisplayShowTitleEnabled(false);
Tab tab;
tab = actionBar.newTab()
.setText(R.string.VarsTab)
.setTabListener(new TabListener<VarValues>(
this, "VarValues", VarValues.class));
actionBar.addTab(tab);
tab = actionBar.newTab()
.setText(R.string.SecTab)
.setTabListener(new TabListener<SecFrag>(
this, "SecFrag", SecFrag.class));
actionBar.addTab(tab);
}
}
TabListener
package rb.rfrag.namespace;
import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.ActionBar.Tab;
public class TabListener<T extends Fragment> implements ActionBar.TabListener {
private Fragment 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 = 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);
}
}
}
I've resolved using onSaveInstanceState and onRestoreInstanceState in the Activity to maintain the selected tab and modifying onTabSelected as follows.
The last modify avoids that Android rebuild the Fragment it doesn't destoy. However I don't understand why the Activity is destroyed by the rotation event while the current Fragment no. (Have you some idea about this?)
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// previous Fragment management
Fragment prevFragment;
FragmentManager fm = mActivity.getFragmentManager();
prevFragment = fm.findFragmentByTag(mTag);
if (prevFragment != null) {
mFragment = prevFragment;
} // \previous Fragment management
// 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);
}
}
Since I use a android.support.v4.view.ViewPager overriding onTabSelected would not help. But still you hint pointed me in the right direction.
The android.support.v4.app.FragmentManager saves all fragments in the onSaveInstanceState of the android.support.v4.app.FragmentActivity. Ignoring setRetainInstance — Depending on your design this might lead to duplicate fragments.
The simplest solution is to delete the saved fragment in the orCreate of the activity:
#Override
public void onCreate (final android.os.Bundle savedInstanceState)
{
if (savedInstanceState != null)
{
savedInstanceState.remove ("android:support:fragments");
} // if
super.onCreate (savedInstanceState);
…
return;
} // onCreate
The solution does not actually require a whole lot of work, the following steps ensure, that when rotating the screen, the tab selection is maintained. I ran into overlapping Fragments, because upon screen rotation my first tab was selected, not the second one that was selected before rotating the screen and hence the first tab was overlapping the content of the second tab.
This is how your Activity should look (I am using ActionBarSherlock but adjusting it should be very easy):
public class TabHostActivity extends SherlockFragmentActivity {
private static final String SELETED_TAB_INDEX = "tabIndex";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Setup the action bar
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Create the Tabs you need and add them to the actionBar...
if (savedInstanceState != null) {
// Select the tab that was selected before orientation change
int index = savedInstanceState.getInt(SELETED_TAB_INDEX);
actionBar.setSelectedNavigationItem(index);
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save the index of the currently selected tab
outState.putInt(SELETED_TAB_INDEX, getSupportActionBar().getSelectedTab().getPosition());
}
}
And this is what my ActionBar.TabListener looks like (its a private class in the above Activity):
private class MyTabsListener<T extends Fragment> implements ActionBar.TabListener {
private Fragment fragment;
private final SherlockFragmentActivity host;
private final Class<Fragment> type;
private String tag;
public MyTabsListener(SherlockFragmentActivity parent, String tag, Class type) {
this.host = parent;
this.tag = tag;
this.type = type;
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction transaction) {
/*
* The fragment which has been added to this listener may have been
* replaced (can be the case for lists when drilling down), but if the
* tag has been retained, we should find the actual fragment that was
* showing in this tab before the user switched to another.
*/
Fragment currentlyShowing = host.getSupportFragmentManager().findFragmentByTag(tag);
// Check if the fragment is already initialised
if (currentlyShowing == null) {
// If not, instantiate and add it to the activity
fragment = SherlockFragment.instantiate(host, type.getName());
transaction.add(android.R.id.content, fragment, tag);
} else {
// If it exists, simply attach it in order to show it
transaction.attach(currentlyShowing);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction fragmentTransaction) {
/*
* The fragment which has been added to this listener may have been
* replaced (can be the case for lists when drilling down), but if the
* tag has been retained, we should find the actual fragment that's
* currently active.
*/
Fragment currentlyShowing = host.getSupportFragmentManager().findFragmentByTag(tag);
if (currentlyShowing != null) {
// Detach the fragment, another tab has been selected
fragmentTransaction.detach(currentlyShowing);
} else if (this.fragment != null) {
fragmentTransaction.detach(fragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction fragmentTransaction) {
// This tab is already selected
}
The above implementation also allows replacement of Fragments within a tab, based on their tags. For this purpose when switching fragments within the same tab I use the same Tag name, that was used for the initial framework that's been added to the tab.
Thanks Martin and asclepix for theirs solutions. I have 3 tabs and first tab contains 2 fragments, like this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".MainActivity" >
<FrameLayout
android:id="#+id/frActiveTask"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
/>
<FrameLayout
android:id="#+id/frTaskList"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_above="#id/frActiveTask"
/>
</RelativeLayout>
Using onRestoreInstanceState, onSaveInstanceState and savedInstanceState.remove("android:support:fragments"); methods and statement working almost fine except if your active tab is not the first one and rotate and click on first, a clear display appears and only for the second click on the first tab came the right fragment display.
After debugging code I recognized that the first addTab always calls an onTabSelected event in the tab listener, with a fragment add method and then when the setSelectedNavigationItem is called from onRestoreInstanceState a detach is executed on the first tab and an add for other one.
This unecessary add calling is fixed in my solution.
My activity
protected void onCreate(Bundle savedInstanceState) {
boolean firstTabIsNotAdded = false;
if (savedInstanceState != null) {
savedInstanceState.remove("android:support:fragments");
firstTabIsNotAdded = savedInstanceState.getInt(SELETED_TAB_INDEX) != 0;
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// codes before adding tabs
actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
tabStartAndStop = actionBar.newTab().setText(getString(R.string.tab_title_start_and_stop))
.setTabListener(
new FragmentTabListener<StartStopFragment>(this,
getString(R.string.tab_title_start_and_stop_id),
StartStopFragment.class,
firstTabIsNotAdded));
tabHistory = actionBar.newTab().setText(getString(R.string.tab_title_history))
.setTabListener(
new FragmentTabListener<HistoryFragment>(this,
getString(R.string.tab_title_history_id),
HistoryFragment.class,
false));
tabRiporting = actionBar.newTab().setText(getString(R.string.tab_title_reporting))
.setTabListener(
new FragmentTabListener<ReportingFragment>(this,
getString(R.string.tab_title_reporting_id),
ReportingFragment.class,
false));
actionBar.addTab(tabStartAndStop);
actionBar.addTab(tabHistory);
actionBar.addTab(tabRiporting);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
int index = savedInstanceState.getInt(SELETED_TAB_INDEX);
actionBar.setSelectedNavigationItem(index);
}
super.onRestoreInstanceState(savedInstanceState);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save the index of the currently selected tab
outState.putInt(SELETED_TAB_INDEX, getSupportActionBar().getSelectedTab().getPosition());
}
And the modified tab listener
public class FragmentTabListener<T extends SherlockFragment> implements com.actionbarsherlock.app.ActionBar.TabListener {
private Fragment mFragment;
private final Activity mFragmentActivity;
private final String mTag;
private final Class<T> mClass;
private boolean doNotAdd;
/** 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 FragmentTabListener(Activity activity, String tag, Class<T> clz, boolean doNotAdd) {
mFragmentActivity = activity;
mTag = tag;
mClass = clz;
this.doNotAdd = doNotAdd;
}
/* 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
if(doNotAdd){
doNotAdd = false;
}else{
mFragment = Fragment.instantiate(mFragmentActivity, 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.
}
}

Categories

Resources