faizal
faizal

Reputation: 3565

maintain Item selection on orientation change

I am trying to restore the state of the action mode on orientation change. But it does not work properly. Following the below sequence of steps, in #3 and #4 the selections get corrupted :

  1. Long click an item - item is highlighted, contextual action bar shows with title shows "1 selected". Looks good.
  2. Turn the phone to landscape - no change. Looks good.
  3. Turn the phone back to portrait - item is not highlighted, contextual action bar is gone
  4. Long click an item - item is highlighted, contextual action bar shows with title shows "0 selected"

myListView.getCheckedItemCount() in onSaveInstanceState() returns 0 on #3. That's where the problem starts.

What am i doing wrong in my fragment(using support library)?

@Override
public void onSaveInstanceState(Bundle outState) {
    //check if any items are selected
    if (myListView.getCheckedItemCount() > 0) {
        //get the list of selected items and convert it to an int Array
        //because SparseBooleanArray cannot be stored in a bundle
        SparseBooleanArray selectedItems = myListView.getCheckedItemPositions();
        int[] selectedItems_intArray = new int[myListView.getCheckedItemCount()];
        for (int i = 0; i < selectedItems.size(); i++) {
            if (selectedItems.valueAt(i) == false)
                continue;
            selectedItems_intArray[i] = selectedItems.keyAt(i);
        }
        outState.putIntArray(KEY_CHECKED_ITEMS, selectedItems_intArray);
    }
}

@Override
public void onViewStateRestored(Bundle savedInstanceState) {
    super.onViewStateRestored(savedInstanceState);
    if (savedInstanceState != null) {
        int[] checkedItems = savedInstanceState.getIntArray(KEY_CHECKED_ITEMS);
        if (checkedItems != null) {
            actionMode = ((ActionBarActivity) getActivity()).startSupportActionMode(new ContextualActionBarActionModeCallBack());

            for (int i = 0; i < checkedItems.length; i++) {
                myListView.setItemChecked(checkedItems[i], true);
            }

            actionMode.setTitle(myListView.getCheckedItemCount() + " selected");
        }
    }
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    myListView = (ListView) getActivity().findViewById(R.id.myListView);
    adpt = new myCustomCursorAdapter(getActivity());
    myListView.setAdapter(adpt);
    //Choice mode is allowed only after a long click
    //disabling it on first time load
    myListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
        myListView.setOnItemLongClickListener(new OnItemLongClickListener(){
        @Override
        public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id){
            if(actionMode == null){
                //Start the action mode
                actionMode = ((ActionBarActivity)getActivity()).startSupportActionMode(new ContextualActionBarActionModeCallBack());

                myLisVIew.setItemChecked(position, true);                           
                actionMode.setTitle(myListView.getCheckedItemCount() + " selected");
                return true;
                }
            else return false;
        }
    });
}

@Override
public void onResume(){
    super.onResume();
    getLoaderManager().initLoader(0,null,this);
}

private class ContextualActionBarActionModeCallBack implements ActionMode.Callback{
    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.mycontextmenu, menu);   
        myListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    }
    ...
    ...
}

ListView layout element :

<ListView
        android:id="@+id/myListView"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:layout_alignParentTop="true"
        android:listSelector="@android:color/transparent"
        android:stackFromBottom="true"
        android:layout_above="@id/layout_input"
        android:divider="#00000000"
        />

Upvotes: 2

Views: 2443

Answers (1)

faizal
faizal

Reputation: 3565

The issue is that the choice mode of the list view is being reset to a default of CHOICE_MODE_NONE every time the list view is created on orientation change because a choice mode has not been explicitly defined in the list view layout file. The Android source where this happens - http://androidxref.com/4.4.2_r2/xref/frameworks/base/core/java/android/widget/AbsListView.java#815.

The solution is to add :

android:choiceMode="multipleChoice"

to the list view layout.

This also means that there is no need to manually store the list view selections in onSaveInstanceState() and retrieve it in onViewStateRestored(). It is done automatically by the fragment. So remove both these functions. Just start the action mode in onResume :

@Override
    public void onResume() {
        super.onResume();
            getLoaderManager().initLoader(0,null,this);
            if (myListView.getCheckedItemCount() > 0 && actionMode == null) {
            actionMode = ((ActionBarActivity) getActivity()).startSupportActionMode(new ContextualActionBarActionModeCallBack());
            actionMode.setTitle(myListView.getCheckedItemCount() + " selected");
        }
    }

Note : If you have implemented the LoaderManager.LoaderCallBacks interface and you need to access the adaptor items just after orientation change, do it in onLoadFinished() after the adaptor has been loaded with the data source. Doing it in onResume() would cause an NPE.

Upvotes: 7

Related Questions