Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fragments Not Working Correctly After Orientation Change

I am having a problem with Fragments and orientation change.

I have an application which has a MainActivity which handles the switching up Fragments via a tabbed action bar. Here is the code for the tabbed action bar and when a tab is selected.

private class MyTabListener <T extends android.support.v4.app.Fragment> implements ActionBar.TabListener {
    private android.support.v4.app.Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    public MyTabListener(SherlockFragmentActivity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }

    @Override
    public void onTabSelected(Tab tab, android.support.v4.app.FragmentTransaction ft) {
        if (mFragment == null){ // check to see if the fragment has already been initialised. If not create a new one.
            mFragment = android.support.v4.app.Fragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content,mFragment,mTag);
        } else {
            ft.attach(mFragment); // if the fragment has been initialised attach it to the current activity
        }
    }

    @Override
    public void onTabUnselected(Tab tab, android.support.v4.app.FragmentTransaction ft) {
        if (mFragment != null){
            ft.detach(mFragment); // when a fragment is no longer needed, detach it from the activity but dont destroy it
        }
    }

    @Override
    public void onTabReselected(Tab tab, android.support.v4.app.FragmentTransaction ft) {

    }

The way I see it is that everything works fine when the application first loads in portrait mode. When I rotate, for some reason another instance of Fragment1 is added and then isn't detached when another tab is selected. This is also the case when I then rotate back to portrait.

I have tried using setRetainInstance(true); in my Fragments but this doesn't work.

Is there a method I need to override or do something with my Fragments before rotating?

Thanks in advance.

EDIT I have now seen that onTabSelected is called again when the activity is recreated. Could this be the cause of my problem with fragments being attached more than once?

Here is my activities onCreate method.

super.onCreate(savedInstanceState);

        if (savedInstanceState == null) {
           //setContentView(R.layout.main);

        }
     // Create the Action Bar with tabs
        ActionBar actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        actionBar.setDisplayShowTitleEnabled(false);
        //create the tab for track and add it to the action bar.
        Tab tab = actionBar.newTab()
                            .setText("Track")
                            .setTabListener(new MyTabListener<TrackingFragment>(this,"track",TrackingFragment.class));
        actionBar.addTab(tab);

        //create the tab for ski tracks and add it to the action bar.
        tab = actionBar.newTab()
                        .setText("Something Tracks")
                        .setTabListener(new MyTabListener<TrackListFragment>(this,"somethingtracks",TrackListFragment.class));
        actionBar.addTab(tab);

        //create the tab for photos and add it to the action bar.
        tab = actionBar.newTab()
                        .setText("Photos")
                        .setTabListener(new MyTabListener<PhotoFragment>(this,"photos",PhotoFragment.class));
        actionBar.addTab(tab);
like image 398
StuStirling Avatar asked Jun 18 '12 14:06

StuStirling


People also ask

What happens when orientation is changed in Android?

When you rotate your device and the screen changes orientation, Android usually destroys your application's existing Activities and Fragments and recreates them. Android does this so that your application can reload resources based on the new configuration.

How to handle screen orientation changes in Android?

If you want to manually handle orientation changes in your app you must declare the "orientation" , "screenSize" , and "screenLayout" values in the android:configChanges attributes. You can declare multiple configuration values in the attribute by separating them with a pipe | character.

What happens when screen rotates Android?

When screen rotates, Android destroys the current activity and recreates it. Which means the Activity methods will be called in following order. When onCreate method is called we call setContentView(R. layout.

Are fragments reusable?

A Fragment represents a reusable portion of your app's UI. A fragment defines and manages its own layout, has its own lifecycle, and can handle its own input events. Fragments cannot live on their own--they must be hosted by an activity or another fragment.


2 Answers

Short fix:

In the onTabSelected method, before using if (mFragment == null) you need to try to get the fragment (using mFragment = getSupportFragmentManager().findFragmentByTag(mTag)). You can also set this from the outside but I don't see you doing this.

Checking if(savedInstanceState == null) on onCreate could also solve this and I consider it a better approach though! :)

like image 195
Pedro Loureiro Avatar answered Oct 26 '22 13:10

Pedro Loureiro


It sounds like you don't have something in your onCreate method wrapped in a if(savedInstanceState == null), so you are creating another fragment in addition to the one being restored from the savedInstanceState bundle.

EDIT

Looking more closely at your code, I think I was wrong about the onCreate, your onTabSelected should handle it. I think your if (mFragment == null) is always coming up null, because you don't try and find the fragment. Change that code section to:

@Override 
public void onTabSelected(Tab tab, android.support.v4.app.FragmentTransaction ft) { 

    mFragment = mActivity.getSupportFragmentManager().findFragmentByTag(mTag);   // add this

    if (mFragment == null){ // check to see if the fragment has already been initialised. If not create a new one. 
        mFragment = android.support.v4.app.Fragment.instantiate(mActivity, mClass.getName()); 
        ft.add(android.R.id.content,mFragment,mTag); 
    } else { 
        ft.attach(mFragment); // if the fragment has been initialised attach it to the current activity 
    } 
} 
like image 25
Barak Avatar answered Oct 26 '22 12:10

Barak