Double fragment rotating Android with ActionBar - android

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

Related

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.

Android Sherlock ActionBar Tab can't detach SupportMapFragment properly

I have an Android project with Sherlock ActionBar and Support Library.
In the onCreate of SherlockFragmentActivity I initialized ActionBar with 2 tabs:
private void configureActionBar(){
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab tab = actionBar.newTab()
.setText("Events")
.setTabListener(new TabListener<SampleListFragment>(
this, "events", SampleListFragment.class));
actionBar.addTab(tab);
tab = actionBar.newTab()
.setText("Map")
.setTabListener(new TabListener<SupportMapFragment>(
this, "map", SupportMapFragment.class));
actionBar.addTab(tab);
}
The problem is when I go back to the first tab, the screen is black with grayed Map controls. If I turn off\on the device screen, the Map completely removes and everyting is fine.
TabListener implementation is from the Google Manual:
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(ActionBar.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(ActionBar.Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
// Detach the fragment, because another one is being attached
ft.detach(mFragment);
}
}
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
// User selected the already selected tab. Usually do nothing.
}
}
I came across with similar issue once when implementing the SupportMapFragment with a SlidingMenu, after searching forums I found that it was an issue with surfaceView and that it could be fixed by setting the map transparent with this method:
private void setMapTransparent(ViewGroup group) {
int childCount = group.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = group.getChildAt(i);
if (child instanceof ViewGroup) {
setMapTransparent((ViewGroup) child);
} else if (child instanceof SurfaceView) {
child.setBackgroundColor(0x00000000);
}
}
}
Just call that method on your SupportMapFragment's onCreateView.

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

Actionbar with Fragment tabs and AdMob

I have an app that uses the ActionBar with tabs in combination with Fragments.
Now I would like to separate the screen into the normal screen at the top, and a small bar at the bottom for the ads:
Left is the normal screen, the tabs and their Fragments take up the whole screen.
What I want is the situation on the right. The tabs and Fragments take up the red part, the green part is for ads.
So the red part should make room for the ads, I don't want to overlay the ads.
As the Activity which sets up the ActionBar and tabs has no layout, I'm not able to add the AdView.
How can I do this?
Edit
This is how I implemented my app. The actionbar with tabs takes care of showing the fragments, so no xml layout file is used in the main Activity.
My code:
TestActivity.java
public class TestActivity extends SherlockFragmentActivity {
private ActionBar actionBar;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupTabs(savedInstanceState);
initAds();
}
private void setupTabs(Bundle savedInstanceState) {
actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
addTab1();
addTab2();
}
private void addTab1() {
Tab tab1 = actionBar.newTab();
tab1.setTag("1");
String tabText = "1";
tab1.setText(tabText);
tab1.setTabListener(new TabListener<MyFragment>(TestActivity.this, "1", MyFragment.class));
actionBar.addTab(tab1);
}
private void addTab2() {
Tab tab1 = actionBar.newTab();
tab1.setTag("2");
String tabText = "2";
tab1.setText(tabText);
tab1.setTabListener(new TabListener<MyFragment>(TestActivity.this, "2", MyFragment.class));
actionBar.addTab(tab1);
}
private void initAds(){
//Here I want to display the ad, only loading once, Just like Davek804 said
}
}
TabListener.java
public class TabListener<T extends SherlockFragment> implements com.actionbarsherlock.app.ActionBar.TabListener {
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;
}
/* The following are each of the ActionBar.TabListener callbacks */
public void onTabSelected(Tab tab, FragmentTransaction ft) {
SherlockFragment preInitializedFragment = (SherlockFragment) mActivity.getSupportFragmentManager().findFragmentByTag(mTag);
// Check if the fragment is already initialized
if (preInitializedFragment == null) {
// If not, instantiate and add it to the activity
SherlockFragment mFragment = (SherlockFragment) SherlockFragment.instantiate(mActivity, mClass.getName());
ft.add(android.R.id.content, mFragment, mTag);
} else {
ft.attach(preInitializedFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
SherlockFragment preInitializedFragment = (SherlockFragment) mActivity.getSupportFragmentManager().findFragmentByTag(mTag);
if (preInitializedFragment != null) {
// Detach the fragment, because another one is being attached
ft.detach(preInitializedFragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// User selected the already selected tab. Usually do nothing.
}
}
MyFragment.java
public class MyFragment extends SherlockFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.myfragment, container, false);
}
}
Create an XML file containing the AdView definition and use <include> to include it at the bottom of each of your fragments.
Alternatively create a layout and your tabs to it. Cf. the reference dos:
To get started, your layout must include a ViewGroup in which you
place each Fragment associated with a tab. Be sure the ViewGroup has a
resource ID so you can reference it from your tab-swapping code.
Alternatively, if the tab content will fill the activity layout
(excluding the action bar), then your activity doesn't need a layout
at all (you don't even need to call setContentView()). Instead, you
can place each fragment in the default root ViewGroup, which you can
refer to with the android.R.id.content ID (you can see this ID used in
the sample code below, during fragment transactions).
http://android-developers.blogspot.com/2009/02/android-layout-tricks-2-reusing-layouts.html
http://android-developers.blogspot.com/2009/03/android-layout-tricks-3-optimize-by.html
The second link details how to do things like this picture:

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

Categories

Resources