I have read alot on stackoverflow on fragments issues but I cant find a solution to my problem.
I have a Tabhost and when I change rotation of the device and then select another tab, the view from the first tab is also visible. So both tabs content is on top of each other.
I'm using a a custom tablistener and every tab is a fragment. I could bypass this with android:configChanges="keyboardHidden|orientation|screenSize"but this solution gave me a list of other problems and I read that this is a bad solution.
public class TabListener<T extends Fragment> implements ActionBar.TabListener {
private Fragment fragment;
private final FragmentActivity activity;
private final String tag;
private final Class<T> myClass;
private long id;
public TabListener(FragmentActivity a, String t, Class<T> c, long id) {
tag = t;
myClass = c;
activity = a;
this.id = id;
}
/** The following are each of the ActionBar.TabListener callbacks */
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// Check if the fragment is already initialized
if (fragment == null) {
fragment = Fragment.instantiate(activity, myClass.getName());
// Sends stored TimerClass id to fragment
if(id != 0) {
Bundle b = new Bundle();
b.putLong("id", id);
fragment.setArguments(b);
}
ft.add(android.R.id.content, fragment, tag);
} else { // If it exists, simply attach it in order to show it
ft.attach(fragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (fragment != null)
ft.detach(fragment);
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
editNameDialog();
}
}
I dont know it this should be handled in the fragment, the activity or in the TabListener. The tabs content is viewd correctly until I change the screens orientation.
I found parts of the answer here on stackoverflow.
Solution
This is how I solved the problem. In the tab listeners I put:
fragment = activity.getSupportFragmentManager().findFragmentByTag(tag);
in both onTabSelect() and onTabUnselect()
Select correct tab after orientation change
When the screen rotates the activity is recreated(?) we need to store the last selected tab index.
Save last selected tab (this goes in the activity):
#Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putInt("lastTab", actionBar.getSelectedNavigationIndex());
}
To load tab index, put this in onCreate() of the activity, this way we retreive the last tab index and selects it:
if (savedInstanceState != null) {
actionBar.selectTab(actionBar.getTabAt(savedInstanceState.getInt("lastTab")));
}
Initialize controllers in the fragment
To prevent the controls to diplay and behave odd I moved all controls inits in the fragment to onCreateView() like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.timer, container, false);
initializeControls(v);
setSeekBars(v);
return v;
}
I hope this will help someone else out there.
Related
I have a ListView inside of a Fragment attached to a CursorAdapter. The Fragment has setRetainInstance(true). Under the Fragment's onCreate() method, I instantiate the CursorAdapter (storing it in variable adapter). I then call listView.setAdapter(adapter) under my Fragment's onCreateView method. The Cursor in the CursorAdapter is loaded by a CursorLoader. Inside my LoaderCallbacks' onLoadFinished(), I call adapter.swapCursor(cursor).
In sum: Everything seems to be in place such that the ListView should not scroll to top when changing between tabs and back. But it still does! Could I be missing something?
Here's some code.
Fragment
public class Home extends Fragment{
...
private HomeFeedAdapter adapter; // HomeFeedAdapter extends CursorAdapter
private AutoQuery autoQuery; // AutoQuery extends LoaderCallbacks<Cursor>
... // (See inner class, at the end)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
if(adapter == null)
adapter = new HomeFeedAdapter(getActivity(), null);
if(autoQuery == null)
autoQuery = new AutoQuery();
getLoaderManager().initLoader(LOADER_INITIAL, null, autoQuery);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Layout
View v = inflater.inflate(R.layout.home, container, false);
l = (ListView) v.findViewById(R.id.listview);
l.setAdapter(adapter);
return v;
}
private class AutoQuery implements LoaderCallbacks<Cursor>{
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
...
return new CursorLoader(getActivity(), uri,
null, null, null, null);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
adapter.swapCursor(cursor);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
}
}
Activity
public class MainActivity extends SherlockFragmentActivity {
...
private class TabsListener implements ActionBar.TabListener {
private Fragment fragment;
private String tag;
public TabsListener(Fragment fragment, String tag) {
this.fragment = fragment;
this.tag = tag;
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// Do nothing
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft.replace(R.id.fragment_container, fragment, tag);
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.remove(fragment);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Layout
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setHomeButtonEnabled(false);
getSupportActionBar().setDisplayShowTitleEnabled(false);
setContentView(R.layout.activity_main);
// Loads fragment
Fragment fHome, fActivity, fProfile;
if((fHome = getSupportFragmentManager().findFragmentByTag(HOME)) == null) fHome = new Home();
if((fActivity = getSupportFragmentManager().findFragmentByTag(ACTIVITY)) == null) fActivity = new FriendsList();
if((fProfile = getSupportFragmentManager().findFragmentByTag(PROFILE)) == null) fProfile = new Profile();
ActionBar.Tab tab;
tab = getSupportActionBar().newTab();
tab.setTabListener(new TabsListener(
fHome,
HOME
));
getSupportActionBar().addTab(tab, false);
tab = getSupportActionBar().newTab();
tab.setTabListener(new TabsListener(
fActivity,
ACTIVITY
));
getSupportActionBar().addTab(tab, false);
tab = getSupportActionBar().newTab();
tab.setTabListener(new TabsListener(
fProfile,
PROFILE
));
getSupportActionBar().addTab(tab, false);
...
}
}
With Greg Giacovelli's help in leading me in the right direction, I've found a solution to my problem.
I'll begin with a disclaimer that I don't quite understand how ListView positions are saved. My ListView instance is recreated every time that my Fragment's onCreateView() is called. This happens when the screen rotates, for example. And yet, in the specific case of screen rotations, even though onCreateView() is called and thus the ListView is reinstantiated, the ListView's state is nonetheless restored. So if the ListView is being recreated, something else must be telling it what its position previously was... and I don't know what that is. I think that it's attributable to the machinery of setRetainInstance(true).
But let's look at my main issue: Why did the ListView scroll to top between tab changes? As Greg suggested, it was because I was re-adding the Fragment with replace(), and thus destroying my Fragment and re-creating every time the user flipped to another tab and back.
My solution was to check if the tab was already added; if so, then not add it; else, add it. Then, when the user clicks out of a tab, I simply detach the fragment (not remove it), and attach a new one. This way the unselected tab's Fragment is still alive, though detached.
// Tabs listener
private class TabsListener implements ActionBar.TabListener {
private Fragment fragment;
private String tag;
public TabsListener(Fragment fragment, String tag) {
this.fragment = fragment;
this.tag = tag;
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
if(fragment instanceof ScrollableToTop) ((ScrollableToTop) fragment).scrollToTop();
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if(!fragment.isAdded()){
ft.add(R.id.fragment_container, fragment, tag);
}
ft.attach(fragment);
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.detach(fragment);
}
}
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
I've done stuff like this before and this is a super simple example but I can't seem to get it working using actionbarsherlock v4.1.
This is the main activity
public class VanityActivity extends SherlockFragmentActivity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
//restore
}
com.actionbarsherlock.app.ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setHomeButtonEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
com.actionbarsherlock.app.ActionBar.Tab tab1 = actionBar.newTab();
tab1.setText("Website");
tab1.setTabListener(new HeaderTabListener(getApplicationContext()));
tab1.setTag(1);
actionBar.addTab(tab1);
com.actionbarsherlock.app.ActionBar.Tab tab2 = actionBar.newTab();
tab2.setText("Portfolio");
tab2.setTabListener(new HeaderTabListener(getApplicationContext()));
tab2.setTag(2);
actionBar.addTab(tab2);
com.actionbarsherlock.app.ActionBar.Tab tab3 = actionBar.newTab();
tab3.setText("Team");
tab3.setTabListener(new HeaderTabListener(getApplicationContext()));
tab3.setTag(3);
actionBar.addTab(tab3);
}
}
And my HeaderTabListener class:
public class HeaderTabListener implements
com.actionbarsherlock.app.ActionBar.TabListener {
private Context context;
public HeaderTabListener(Context context){
this.context = context;
}
#Override
public void onTabSelected(com.actionbarsherlock.app.ActionBar.Tab tab, android.support.v4.app.FragmentTransaction ft) {
int i=((Integer)tab.getTag()).intValue();
if(tab.getPosition()==0){
ft.replace(android.R.id.content, CompanyFragment.newInstance(i));
}else if(tab.getPosition()==1){
ft.replace(android.R.id.content, PortfolioFragment.newInstance(i));
}
}
#Override
public void onTabUnselected(com.actionbarsherlock.app.ActionBar.Tab tab, android.support.v4.app.FragmentTransaction ft) {
Log.v("Tab selected", tab.getText().toString());
}
#Override
public void onTabReselected(com.actionbarsherlock.app.ActionBar.Tab tab, android.support.v4.app.FragmentTransaction ft) {
Log.v("Tab selected2", tab.getText().toString());
}
}
And finally one of the fragments (both fragments are the same but the text in each layout is different)
public class PortfolioFragment extends SherlockFragment {
private static final String KEY_POSITION="position";
private static final String KEY_TEXT="text";
static PortfolioFragment newInstance(int position) {
PortfolioFragment frag=new PortfolioFragment();
Bundle args=new Bundle();
args.putInt(KEY_POSITION, position);
frag.setArguments(args);
return(frag);
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
View result=inflater.inflate(R.layout.fragment_portfolio, container, false);
int position=getArguments().getInt(KEY_POSITION, -1);
return(result);
}
}
So when it first loads the the tabs are all displayed correctly, the first one his highlighted and the text in CompanyFragment layout displays. But then I am unable to click any of the other tabs to change the content and my logs inside of onTabSelected just never gets called. Any thoughts? Thanks for reading.
First off, let me tell you that I have no experience with ActionBarSherlock. I looked at your code to get a few ideas for my application. Seeing as it hasn't been answered since you posted it a few month ago, I would like to share my first impression.
As far as I know, ActionBarSherlock exposes the same functionality as the most recent Android APIs. Therefore, I do think that you are missing a simple method call. You would need to call commit() (Android Developer Guide reference) after you replace the fragment. Therefore, change the line, and all similar lines from;
ft.replace(android.R.id.content, PortfolioFragment.newInstance(i));
to
ft.replace(android.R.id.content, PortfolioFragment.newInstance(i)).commit();.
I hope this will solve your problem although I do realise you state that the ònTabSelected()` method is never called. Without debugging the difference between the problems would, however, seem transparent.
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:
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.
}
}