この問題は、AbsListView.java の次の柔軟性のないコードに起因します。これは、最後の項目が選択解除されると、ActionMode を無条件に破棄します。
class MultiChoiceModeWrapper implements MultiChoiceModeListener {
...
@Override
public void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked) {
mWrapped.onItemCheckedStateChanged(mode, position, id, checked);
// If there are no items selected we no longer need the selection mode.
if (getCheckedItemCount() == 0) {
mode.finish();
}
}
}
AbsListView は、この手の込んだ動作を合法的に阻止するためのフックを提供しないため、上記のコードによって呼び出されたときに、独自の AbsListView サブクラスから getCheckedItemCount から偽の値を返すという醜いハックに頼らざるを得なくなります。
public class ActionModeHackGridView extends GridView {
...
@Override
public int getCheckedItemCount() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
String name = ActionModeHackGridView.class.getName();
for (int i = 0; i < stackTrace.length; i++) {
// First find this method in the stack
if (stackTrace[i].getClassName().equals(name)
&& stackTrace[i].getMethodName().equals("getCheckedItemCount")) {
// ...then examine the caller of this method
if (stackTrace[i + 1].getClassName().equals(
"android.widget.AbsListView$MultiChoiceModeWrapper")
&& stackTrace[i + 1].getMethodName().equals("onItemCheckedStateChanged")) {
// Return a non-zero value so AbsListView doesn't dismiss the ActionMode
return 1;
}
break;
}
}
return super.getCheckedItemCount();
}
}
AbsListView サブクラス (ListView など) でも同じハックを使用できます。