Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ListView selection remains persistent after exiting choice mode

I have a ListView subclass that I allow selections on when the context action bar (CAB) is active. The CAB is set as a callback to the onItemLongClick event:

public boolean onCreateActionMode(ActionMode mode, Menu menu) {
    // Inflate a menu resource providing context menu items
    MenuInflater inflater = mode.getMenuInflater();
    inflater.inflate(context_menu, menu);
    getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    return true;
}

This is fine, and the ListView works as expected, with the currently selected item staying highlighted when touched.

When I close the CAB, I want the ListView to return to normal (i.e. Touch mode). The problem is that the last selected item remains highlighted indefinitely, regardless of what methods I try to clear it:

public void onDestroyActionMode(ActionMode mode) {
    //Unselect any rows
    ListView lv = getListView();
    lv.clearChoices(); // Has no effect
    lv.setChoiceMode(ListView.CHOICE_MODE_NONE); // Has no effect on the highlighted item 
    lv.setFocusable(false); // Has no effect
    lv.setSelection(0); // Has no effect
    mActionMode = null;
}

Any suggestions?

like image 356
CjS Avatar asked Mar 17 '12 22:03

CjS


5 Answers

The main reason for the problem is that once the ListView selection mode is switched to CHOICE_MODE_NONE, the framework optimizes out the clear operation as it is no longer supporting 'selections'. I have improved the above workarounds a bit by clearing the selection state manually and then setting the mode in a delayed manner so the framework will have its turn to clear the state before turning the mode to CHOICE_MODE_NONE.

final ListView lv = getListView();
lv.clearChoices();
for (int i = 0; i < lv.getCount(); i++)
    lv.setItemChecked(i, false);
lv.post(new Runnable() {
    @Override
    public void run() {
        lv.setChoiceMode(ListView.CHOICE_MODE_NONE);
    }
});
like image 100
Rudi Avatar answered Nov 10 '22 18:11

Rudi


I faced the same issue and since requesting layout doesn't solve the problem for me either I implemented a little hack which works for me. Maybe this is the same issue because I'm switching between CHOICE_MODE_SINGLE and CHOICE_MODE_NONE.

When the action mode ends I'm calling this code snippet. clearChoices makes sure that all items are not checked anymore (internally). The iteration over the views makes sure that all currently visible views are reset and not checked anymore.

mListView.clearChoices();

for (int i = 0; i < mListView.getChildCount(); i++) {
    ((Checkable) mListView.getChildAt(i)).setChecked(false);
}

mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
like image 30
Knickedi Avatar answered Nov 10 '22 18:11

Knickedi


Looking at the ListView sourcecode, the only way to work around this is to set the ListView to CHOICE_MODE_NONE, then re-assign the ListAdapter (which clears the internal selection list regardless of choice mode)

i.e. in a ListFragment/ListActivity

getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);
getListView().setAdapter(getListAdapter())
like image 18
Migaloo Avatar answered Nov 10 '22 17:11

Migaloo


I was having this issue in API Level 17 and solved it by doing:

listView.clearChoices();
listView.invalidateViews();
like image 4
jpmcosta Avatar answered Nov 10 '22 18:11

jpmcosta


For me, it seems the accepted answer is not working for invisible items, and it's no need to call

for (int i = 0; i < lv.getCount(); i++)
        lv.setItemChecked(i, false);

instead, just call

lv.requestLayout();

To completely solve my issue, I call

lv.clearChoices();
lv.requestLayout();

in onDestroyActionMode()

and call

lv.setItemChecked(position, false)

in onItemClick() when it's not in ActionMode

However, I did not confirm whether call setItemChecked() will result some performance issues

like image 2
darktiny Avatar answered Nov 10 '22 16:11

darktiny