88
public class MainActivity extends Activity implements MainMenuFragment.OnMainMenuItemSelectedListener {

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

    FragmentManager fragmentManager = getFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager
            .beginTransaction();

    // add menu fragment
    MainMenuFragment myFragment = new MainMenuFragment();
    fragmentTransaction.add(R.id.menu_fragment, myFragment);

    //add content
    DetailPart1 content1= new DetailPart1 ();
    fragmentTransaction.add(R.id.content_fragment, content1);
    fragmentTransaction.commit();

}
public void onMainMenuSelected(String tag) {
  //next menu is selected replace existing fragment
}

I have a need to display two list views side by side, menu on left and its content on right side. By default, the first menu is selected and its content is displayed on right side. The Fragment that displays content is as below:

public class DetailPart1 extends Fragment {
  ArrayList<HashMap<String, String>> myList = new ArrayList<HashMap<String, String>>();
  ListAdapter adap;
  ListView listview;

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

       if(savedInstanceState!=null){
        myList = (ArrayList)savedInstanceState.getSerializable("MYLIST_obj");
        adap = new LoadImageFromArrayListAdapter(getActivity(),myList );
        listview.setAdapter(adap);
       }else{
        //get list and load in list view
        getlistTask = new GetALLListTasks().execute();
    }


     @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.skyview_fragment, container,false);
           return v;
        }


     @Override
      public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
          outState.putSerializable("MYLIST_obj", myList );
        }
    }

The onActivityCreated and onCreateView are called twice. There are many examples out there using fragments. Since I am beginner in this subject, I am unable relate the example with my problem. I need a fool proof way to handle orientation change. I have NOT declared android:configChanges in manifest file. I need the activity destroy and recreate so that I can use different layout in landscape mode.

Daniel
  • 400
  • 1
  • 2
  • 12
user1811741
  • 883
  • 1
  • 7
  • 5
  • Usually fragments orientation changes are taken care of without you needing doing anything. So, what is the problem you experience when you change the orientation? – Alexander Kulyakhtin Nov 09 '12 at 10:11
  • Alex I need the class DetailPart1 fragment to display layout with listView in portrait mode and layout with Grid View in landscape mode.for this, i need to recreate the fragment every time but i don't want to load the same records again ,so i store them at onSaveInstanceState .But the above code recreates DetailPart1 twice ,I need to know the missing code that makes it work as desired – user1811741 Nov 09 '12 at 18:49
  • possible duplicate of [Android Fragment lifecycle over orientation changes](http://stackoverflow.com/questions/8474104/android-fragment-lifecycle-over-orientation-changes) – naXa stands with Ukraine Dec 18 '14 at 14:24

2 Answers2

133

You are creating a new fragment every time you turn the screen in your activity onCreate(); But you are also maintaining the old ones with super.onCreate(savedInstanceState);. So maybe set tag and find the fragment if it exists, or pass null bundle to super.

This took me a while to learn and it can really be a pain when you are working with stuff like viewpager.

I'd recommend you to read about fragments an extra time as this exact topic is covered.

Here is an example of how to handle fragments on a regular orientation change:

Activity:

public class MainActivity extends FragmentActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        if (savedInstanceState == null) {
            TestFragment test = new TestFragment();
            test.setArguments(getIntent().getExtras());
            getSupportFragmentManager().beginTransaction().replace(android.R.id.content, test, "your_fragment_tag").commit();
        } else {
            TestFragment test = (TestFragment) getSupportFragmentManager().findFragmentByTag("your_fragment_tag");
        }
    }
}

Fragment:

public class TestFragment extends Fragment {

    public static final String KEY_ITEM = "unique_key";
    public static final String KEY_INDEX = "index_key";
    private String mTime;
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_layout, container, false);
        
        if (savedInstanceState != null) {
            // Restore last state
            mTime = savedInstanceState.getString("time_key");
        } else {
            mTime = "" + Calendar.getInstance().getTimeInMillis();
        }
        
        TextView title = (TextView) view.findViewById(R.id.fragment_test);
        title.setText(mTime);
        
        return view;
    }
    
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("time_key", mTime);
    }
}
Troll
  • 1,895
  • 3
  • 15
  • 34
Warpzit
  • 27,966
  • 19
  • 103
  • 155
  • thanks Warpzit for your effort ,but i will be glad if you explain in code about your point.How to alter my code to handle orientation change without reloading the fragment. – user1811741 Nov 09 '12 at 17:45
  • 1
    Hence i need layout of the fragment to be changed on orientation ,but i do not want to reload all the records ,what i did was 1. add android:configchanges="orientation" in manifest 2. override onConfigurationChanged in fragment and detect the orientation and hide/show listview or gridview depending on orientation .This is the right way ? – user1811741 Nov 10 '12 at 06:17
  • @user1811741 updated with example. I hope it clarifies everything. – Warpzit Nov 10 '12 at 12:15
  • @Warpzit How can handled same scenario in case of ViewPager Fragment implementation since ViewPager take care of all displayed fragment..any suggestion. – CoDe Dec 30 '14 at 12:47
  • 1
    @Shubh well I believe viewpager handles this properly, if not you'll have to override the getItem implementation to fit your needs. Hope that pointed you in the right direction, if not put up a proper question :) – Warpzit Jan 02 '15 at 09:22
  • In the docs, it says "In order for the Android system to restore the state of the views in your activity". So, if you add the fragment dynamically (not via the activity xml), I would expect it's not saved as there's no 'id' set to it. But it's saved somehow as you also has given an example above... – stdout Apr 06 '19 at 13:38
  • Well it kinda depends if they are attached to an activity or not. If they have been added the parent activity will keep them in memory with the sharedinstancestate. If you called super.onCreate(null); in the onCreate of the activity they would be lost. – Warpzit Apr 08 '19 at 10:45
  • @user1811741 This implementation is simple and works well in the [_Coinverse_ app](https://play.google.com/store/apps/details?id=app.coinverse)! I overrode `onConfigurationChanged` in a **DialogFragment** in order to have a **ExoPlayer** and embedded **YouTube** player view seamlessly play content without interruption from an orientation change. In `onConfigurationChanged` I updated the **DialogFragment** dimensions to keep the content proportional to the screen orientation. – AdamHurwitz May 24 '19 at 17:58
21

A good guideline about how to retain data between orientation changes and activity recreation can be found in android guidelines.

Summary:

  1. make your fragment retainable:

    setRetainInstance(true);
    
  2. Create a new fragment only if necessary (or at least take data from it)

    dataFragment = (DataFragment) fm.findFragmentByTag("data");
    
    // create the fragment and data the first time
    if (dataFragment == null) {
    
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Sergej Werfel
  • 1,335
  • 2
  • 14
  • 25