48

I have developed an app in Honeycomb and I am using fragments.
This is my app

  • I have an Activity (Say A1) and in that there is a fragment
  • Initially this fragment hold the object one fragment object say (F1)
  • Then depending on the user actions it may change to other objects F2,F3 ....

What my problem is

When The user rotate the device the activity is recreated and which make F1 as the fragment object even though before rotating it wasn't
What is the way to retain the fragment object while rotating?
I used setRetainInstance(true); but it didn't work for me
And I have added the fragment by code in my onCreate function like this

@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.main);

   FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();

   Fragment homeFragment = new Home();
   fragmentTransaction.add(R.id.mainFragement, homeFragment);
   fragmentTransaction.commit();
}
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Jithin
  • 1,745
  • 4
  • 18
  • 25

6 Answers6

70

By default Android will retain the fragment objects. In your code you are setting the homeFragment in your onCreate function. That is why it is allways some homeFragment or fl what ever that you set in onCreate.

Because whenever you rotate, the onCreate will execute and set your fragment object to the first one

So the easy solution for you is check whether savedInstanceState bundle is null or not and set the fragment object

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if(null == savedInstanceState) {
        // set you initial fragment object 
    }
 }
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Labeeb Panampullan
  • 34,521
  • 28
  • 94
  • 112
  • 1
    what should i do in case i use an activity with ViewPager which handles the fragments? – android developer Aug 17 '12 at 22:56
  • @androiddeveloper pray. No seriously what I did was: super.onCreate(null); – Warpzit Nov 09 '12 at 10:53
  • 30
    “ By default android will retain the fragment objects ” … not true. Android will create new objects on orientation changes, unless `setRetainInstance(true)` is called. Do not confuse this with Android automatically reattaching fragments. They are not the same thing. Check the traces reported by S.D. on [this answer](http://stackoverflow.com/a/12641575/2619107). – Rafa Viotti Nov 14 '13 at 20:58
41

You need to give your Fragment a unique tag, and check whether this Fragment is already added to your Activity already or not.

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    String tag = "my_fragment";
    FragmentManager fragmentManager = getFragmentManager();
    if(fragmentManager.findFragmentByTag(tag) == null) {
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        Fragment homeFragment = new Home();
        fragmentTransaction.add(R.id.mainFragement, homeFragment, tag);
        fragmentTransaction.commit();
    }
}

Checking whether savedInstanceState is null is not a safe way to check whether your fragment is already set - it will work in most cases, but in some cases (such as when the device is on low memory), Android may kill your Activity, which could break your application.

To see this in action, tick "Don't keep activities" in the device's development options (the setting is available in Android 4.0+, not sure about earlier versions). When you open a new activity, your first activity is destroyed. When you return to it (by pressing back), it is created again, and savedInstanceState is not null. However, your fragment is not in the activity anymore, and you have to add it again.

EDIT - Showing the original principle but with SupportFragmentManager

public class ActivityAwesome extends AppCompatActivity
{
    private final String TAG = getClass().getSimpleName();
    private FragmentHome mHomeFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_layout);

        FragmentManager fragmentManager = getSupportFragmentManager();
        Fragment fragment = fragmentManager.findFragmentByTag(TAG);
        if(fragment == null)
        {
            // Create the detail fragment and add it to the activity using a fragment transaction.
            mHomeFragment = new FragmentHome();
            fragmentManager.beginTransaction()
                    .add(R.id.fragment_container, mHomeFragment, TAG)
                    .commit();
        }
        else
        {
            // get our old fragment back !
            mHomeFragment = (FragmentHome)fragment;
        }
    }
}

this comes in especially useful if you want to manipulate the fragment (in this case mHomeFragment) after rotating your device

Someone Somewhere
  • 23,475
  • 11
  • 118
  • 166
Ralf
  • 14,655
  • 9
  • 48
  • 58
  • Whilst I have used the answer provided by @labeeb before, I think checking for the unique `tag` is the best approach. – Craig Russell Dec 15 '15 at 08:30
  • interesting ! It seems then that Google needs to update their template code. – Someone Somewhere Feb 12 '16 at 19:08
  • I believe they should change this lifecycle shit instead, any word found in a dictionary is Android's lifecycle method – Farid Aug 16 '19 at 12:44
  • This tag method solved my problem! Everytime I wanted to say "new HomeFragment" I First checked if I could findByTag a non-null home fragment, if I did find one, I used it, if not, I created a new one :) – Matheos Jan 12 '20 at 11:54
3

Use onAttachFragment() in your Activity to reassign the object:

@Override
public void onAttachFragment(Fragment fragment) {
    if (fragment instanceof MyFragment)
        this.myFragment = (MyFragment) fragment;
}
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Oded Breiner
  • 28,523
  • 10
  • 105
  • 71
0

I defined a Fragment in activity's layout, onSaveInstanceState in the Fragment does get called, but the savedInstanceState Bundle in the Fragment's onCreatView comes as null.

The reason was that my Fragment did not have a ID in XML:

android:id="@+id/compass_fragment" ...
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
zomarev
  • 9
  • 1
0

just rewiring @Ralf answer to be more dynamic, no need to specify a certain fragment to retain, but in case you want to specify, it is also possible :

 public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //set Home/Main/default fragment
        changeFragmentTo(HomeFragment.newInstance(), FRAGMENT_TAG_HOME_FRAGMENT);


        if (getCurrentFragment() != null) {
            //if screen rotated retain Fragment
            changeFragmentTo(getCurrentFragment(), getCurrentFragment().getTag());

        }

    }


    private Fragment getCurrentFragment() {
//fl_main_container is FarmeLayout where I load my Fragments
        return getSupportFragmentManager().findFragmentById(R.id
                .fl_main_container);
    }



    /**
     * changeFragmentTo(Fragment fragmentToLoad, String fragmentTag)
     *
     * @param fragmentToLoad : dataType > v4.app.Fragment :: the object of the fragment you want to load in form of MyFragment() or MyFragment().newInstance()
     * @param fragmentTag    :  dataType > String :: a String which identify the "tag" of the fragment in form of "FRAGMENT_TAG_MY_FRAGMENT", Value must be stored in {@link models.MyConstants}
     */


    public void changeFragmentTo(Fragment fragmentToLoad, String fragmentTag) {

        if (getSupportFragmentManager().findFragmentByTag(fragmentTag) == null) {
            getSupportFragmentManager()
                    .beginTransaction()
                    .replace(R.id.fl_main_container, fragmentToLoad, fragmentTag)
                    .setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
                    .addToBackStack(fragmentTag)
                    .commit();

        } else {
            getSupportFragmentManager()
                    .beginTransaction()
                    .replace(R.id.fl_main_container, fragmentToLoad, fragmentTag)
                    .setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
                    .commit();
        }
    }



}
Joseph Ali
  • 345
  • 1
  • 5
  • 11
-1

You can simply set the RetainInstance property inside OnCreate of the fragment class.

public override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);
    RetainInstance = true;
}

Retain the Fragment object while rotating

Shawinder Sekhon
  • 1,569
  • 12
  • 23