If you call AbsListView.setItemChecked()
directly, it works well, and the ActionMode
will activate and create.
mGridView.setItemChecked(pPosition, true);
But when you call View.startActionMode()
first, then call AbsListView.setItemChecked()
, the ActionMode
create by startActionMode()
will destroy, and recreate a new one by setItemChecked()
.
My question is: How to avoid this issue when call View.startActionMode()
first?
Looking forward to your reply! Thanks!
Why recreate a new one? See the source code of AbsListView.setItemChecked(int position, boolean value)
method, you can see following code:
// Start selection mode if needed. We don't need to if we're unchecking something.
if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
if (mMultiChoiceModeCallback == null ||
!mMultiChoiceModeCallback.hasWrappedCallback()) {
throw new IllegalStateException("AbsListView: attempted to start selection mode " +
"for CHOICE_MODE_MULTIPLE_MODAL but no choice mode callback was " +
"supplied. Call setMultiChoiceModeListener to set a callback.");
}
mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
}
That means if mChoiceActionMode == null
, it will call startActionMode(mMultiChoiceModeCallback)
, so will recreate a new ActionMode.
And how to fix?
Here is a simple way: use reflect to assign a ActionMode create by startActionMode()
to the private field mChoiceActionMode
in AbsListView
.
private void startActionMode() {
// Get the field "mMultiChoiceModeCallback" instance by reflect
AbsListView.MultiChoiceModeListener wrapperIns = null;
try {
Field wrapper = null;
wrapper = AbsListView.class.getDeclaredField("mMultiChoiceModeCallback");
wrapper.setAccessible(true);
wrapperIns = (AbsListView.MultiChoiceModeListener) wrapper.get(mMessageGridView);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// Start the ActionMode, but not select any item.
ActionMode actionMode = mMessageGridView.startActionMode(wrapperIns);
// Assign actionMode to field "mChoiceActionMode" by reflect
try {
Field mChoiceActionMode = null;
mChoiceActionMode = AbsListView.class.getDeclaredField("mChoiceActionMode");
mChoiceActionMode.setAccessible(true);
mChoiceActionMode.set(mMessageGridView, actionMode);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
Why here we use wrapper? Because AbsListView.setMultiChoiceModeListener(MultiChoiceModeListener listener)
will wrap our mMultiChoiceModeListener, so we can't not use directly.