Hi I'm having problems with Action Bar and tabs using ABS. I have two tabs, the first one, "Favourites", just extends Fragment
and does not show anything. The second one, "Lines", extends SherlockListFragment
and implements LoaderManager.LoaderCallbacks
(loads a user list from the database).
The app works ok (or at least, I think so), but the problem appears when I rotate the screen. If I am in portrait mode with LinesTab selected (the list is displayed) and then change to landscape, the content of FavouritesTab (initially empty) is filled with the content of LinesTab.
This is the important part of the code.
Main.java
(create both tabs)
public class Main extends SherlockFragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayShowTitleEnabled(true);
Tab tab = actionBar
.newTab()
.setText("Favourites")
.setTabListener(new FavouritesTab());
actionBar.addTab(tab);
tab = actionBar
.newTab()
.setText("Lines")
.setTabListener(new LinesTab());
actionBar.addTab(tab);
}
}
LinesTab.java
(The tab that load users from database using a LoaderManager)
public class LinesTab extends SherlockListFragment implements ActionBar.TabListener,
LoaderManager.LoaderCallbacks<ArrayList<User>> {
UserAdapter adapter;
Database db;
public void onCreate(Bundle savedInstanceState) {
adapter = new UserAdapter(getActivity(), new ArrayList<User>());
setListAdapter(adapter);
getActivity().getSupportLoaderManager().initLoader(0, null, this);
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
return super.onCreateView(inflater, container, state);
}
@Override
public void onActivityCreated(Bundle state) {
super.onActivityCreated(state);
}
// TabListener callbacks
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
ft.add(android.R.id.content, this, "LinesTabTag");
ft.attach(this);
}
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
ft.detach(this);
}
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {}
// LoaderCallbacks
@Override
public Loader<ArrayList<User>> onCreateLoader(int id, Bundle args) {
db = new Database(getActivity());
return new UserLoader(getActivity(), db);
}
@Override
public void onLoadFinished(Loader<ArrayList<User>> loader,
ArrayList<User> usuarios) {
((UserAdapter) getListAdapter()).update(usuarios);
((UserAdapter) getListAdapter()).notifyDataSetChanged();
}
@Override
public void onLoaderReset(Loader<ArrayList<User>> loader) {
setListAdapter(null);
}
}
FavouritesTab.java
(The other tab, does not show anything)
public class FavouritesTab extends Fragment implements ActionBar.TabListener {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
return super.onCreateView(inflater, container, state);
}
// TabListener callbacks
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
ft.add(android.R.id.content, this, "LinesTabTag");
ft.attach(this);
}
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
ft.detach(this);
}
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {}
}
Furthermore, I'm not quite sure if all the code is implemented in the correct way. Specifically the onTabSelected
method. In the Android documentation they implement a listener that manages all the fragments, but I'm implementing a listener in every fragment. So I don't know if ft.add
and then ft.attach
is the best way to implement the method.
Thanks.
Finally I've found the solution. I've implemented a general TabListener
in Main.java
and save the current tab index everytime the screen is rotated:
Main.java
public class Main extends SherlockFragmentActivity {
int pos = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayShowTitleEnabled(true);
if (savedInstanceState != null) {
// Restore the last tab position
pos = savedInstanceState.getInt("pos");
}
Tab tab = actionBar
.newTab()
.setText("Favourites")
.setTabListener(new TabListener<FavouritesTab>(this, "Favourites", FavouritesTab.class));
//.setTabListener(new FavouritesTab());
actionBar.addTab(tab);
tab = actionBar
.newTab()
.setText("Lines")
.setTabListener(new TabListener<LinesTab>(this, "LinesTab", LinesTab.class));
//.setTabListener(new LinesTab());
actionBar.addTab(tab);
// Set the last tab position, or zero by default
getSupportActionBar().setSelectedNavigationItem(pos);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// Save the current tab position
super.onSaveInstanceState(outState);
outState.putInt("pos", getSupportActionBar().getSelectedNavigationIndex());
}
// The TabListener
static class TabListener<T extends Fragment> implements ActionBar.TabListener {
private Fragment mFragment;
private SherlockFragmentActivity mActivity;
private String mTag;
private Class<T> mClass;
public TabListener(SherlockFragmentActivity 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 = Fragment.instantiate(mActivity, mClass.getName());
ft.add(android.R.id.content, mFragment, mTag);
} else {
// If it exists, simply attach it on order to show it
ft.attach(mFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
ft.detach(mFragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {}
}
}
I've removed the loader callbacks in LinesTab.java
and FavouritesTab.java