2

I have a ListView fragment in my application that is experiencing some issues.

  1. When I scroll to a specific element in the list view, then rotate the device, the list view resets to the top of the list.
  2. When I enter multi-select mode in the list view, then rotate the device, the selected list items reset.

Here is my activity:

public class TestActivity extends ActionBarActivity
{
    protected static final String   FRAGMENT_TAG    = "TEST";

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

        Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FragmentManager fm = getFragmentManager();
        TestFragment f = (TestFragment)fm.findFragmentByTag(FRAGMENT_TAG);

        // If no fragment exists, then create a new one and add it!
        if (f == null)
        {
            fm.beginTransaction().add(R.id.fragment_holder, new TestFragment(), FRAGMENT_TAG)
                    .commit();
        }
    }
}

here is main_layout.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary"
        android:minHeight="?attr/actionBarSize" >
    </android.support.v7.widget.Toolbar>

    <FrameLayout
        android:id="@+id/fragment_holder"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

Here is my TestFragment class, with the misc. content removed:

public class TestFragment extends ListFragment
{
    @Override
    public void onActivityCreated(Bundle savedInstanceState)
    {
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState)
    {
        super.onViewCreated(view, savedInstanceState);

        // Sets the list up for multiple choice selection.
        ListView listView = getListView();
        listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        listView.setMultiChoiceModeListener(this);
    }
}

I have seen two discussions for this. One is stating that I should be using setRetainInstance(true) in the fragment's onCreate(Bundle savedInstanceState) method. The other says I should be using the onSaveInstanceState(Bundle bundle) and onRestoreInstanceState(Bundle bundle) methods to keep track of stuff somehow. I would like to use the setRetainInstanceState(true) approach, but adding that into the project like so:

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

Does not work for me. What am I doing wrong here?

Zach
  • 3,909
  • 6
  • 25
  • 50
  • Retaining the `Fragment` instance is not going to do what you want, as the `Activity` is still going to be recreated, and the views destroyed. If you really want to retain the original views, then you can attempt to retain the `Activity` instance by using the `configChanges` attribute in the manifest definition. However, this is not a good idea unless you really need it, and neither is it reliable, as you can only select specific configuration changes that won't restart the `Activity`, and the `Activity` may still be restarted due to the application process itself being killed and recreated. – corsair992 Jan 31 '15 at 08:47
  • I'd like to avoid using the configChanges attribute, for exactly the reasons you stated. Thanks though. – Zach Feb 02 '15 at 15:09

1 Answers1

3

I would suggest a slightly different approach to adding a Fragment to your Activity.

In your TestActivity class, check if the Bundle is null in the onCreate() method. Only add the Fragment if savedInstanceState is null. This will prevent your Activity from adding another instance of the same Fragment when the device's orientation is changed.

public class TestActivity extends ActionBarActivity {

    protected static final String FRAGMENT_TAG = "TEST";

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

        Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        if (savedInstanceState == null) { 

            getFragmentManager().beginTransaction()
                    .replace(R.id.fragment_holder, new TestFragment(), FRAGMENT_TAG)
                    .commit();
        }

    }

}

I would suggest not using the setRetainInstance(true) method for a Fragment that has a UI. Check the StackOverflow discussion here for an explanation of when to use setRetainInstance(true).

Community
  • 1
  • 1
John P.
  • 4,358
  • 4
  • 35
  • 47
  • In this scenario, am I required to call setListAdapter(data) each time a rotation occurs? I have this setup as described above mocked up in my app but I only see the an activity indicator/spinner and Loading... displayed. However, in a sample app I quickly made that does call setListAdapter() in onActivityCreated(), this seems to work (at least it keeps the location of where I was in the list OK, not sure about selection state of the list items yet). – Zach Feb 02 '15 at 15:02
  • Okay, new update. I have your code working in my app. I am not sure what I was doing wrong, but I suppose that's irrelevant now. I also noticed that the CAB title was not being kept correctly, but I was able to get that back in the `onPrepareActionMode` method, since the activity knows it was open previously, which is nice. – Zach Feb 02 '15 at 16:15
  • Are you by chance overriding onSaveInstanceState() in your Activity and not calling the super method? – John P. Feb 02 '15 at 17:32