Search code examples
androidactionbarsherlockandroid-tabs

Android action bar tabs: screen rotation


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.


Solution

  • 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