Merge "Add selection mode support to GridView."
diff --git a/api/9.xml b/api/9.xml
index 9d8456a..7a4aed0 100644
--- a/api/9.xml
+++ b/api/9.xml
@@ -195395,6 +195395,39 @@
<parameter name="defStyle" type="int">
</parameter>
</constructor>
+<field name="CHOICE_MODE_MULTIPLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_SINGLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<method name="afterTextChanged"
return="void"
abstract="false"
@@ -203851,39 +203884,6 @@
<parameter name="y" type="int">
</parameter>
</method>
-<field name="CHOICE_MODE_MULTIPLE"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHOICE_MODE_NONE"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHOICE_MODE_SINGLE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
</class>
<class name="ListView.FixedViewInfo"
extends="java.lang.Object"
diff --git a/api/current.xml b/api/current.xml
index 4356852..6950d8c 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -215125,6 +215125,17 @@
<parameter name="after" type="int">
</parameter>
</method>
+<method name="clearChoices"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="clearTextFilter"
return="void"
abstract="false"
@@ -215147,6 +215158,61 @@
visibility="public"
>
</method>
+<method name="getCheckedItemCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCheckedItemIds"
+ return="long[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCheckedItemPosition"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCheckedItemPositions"
+ return="android.util.SparseBooleanArray"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getChoiceMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getListPaddingBottom"
return="int"
abstract="false"
@@ -215290,6 +215356,19 @@
visibility="protected"
>
</method>
+<method name="isItemChecked"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
<method name="isScrollingCacheEnabled"
return="boolean"
abstract="false"
@@ -215490,6 +215569,19 @@
<parameter name="views" type="java.util.List<android.view.View>">
</parameter>
</method>
+<method name="setAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.widget.ListAdapter">
+</parameter>
+</method>
<method name="setCacheColorHint"
return="void"
abstract="false"
@@ -215503,6 +215595,19 @@
<parameter name="color" type="int">
</parameter>
</method>
+<method name="setChoiceMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="choiceMode" type="int">
+</parameter>
+</method>
<method name="setDrawSelectorOnTop"
return="void"
abstract="false"
@@ -215542,6 +215647,34 @@
<parameter name="filterText" type="java.lang.String">
</parameter>
</method>
+<method name="setItemChecked"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+<parameter name="value" type="boolean">
+</parameter>
+</method>
+<method name="setMultiChoiceModeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.widget.AbsListView.MultiChoiceModeListener">
+</parameter>
+</method>
<method name="setOnScrollListener"
return="void"
abstract="false"
@@ -215758,6 +215891,50 @@
<parameter name="dr" type="android.graphics.drawable.Drawable">
</parameter>
</method>
+<field name="CHOICE_MODE_MULTIPLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_MULTIPLE_MODAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_SINGLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="TRANSCRIPT_MODE_ALWAYS_SCROLL"
type="int"
transient="false"
@@ -215849,6 +216026,35 @@
</parameter>
</constructor>
</class>
+<interface name="AbsListView.MultiChoiceModeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.view.ActionMode.Callback">
+</implements>
+<method name="onItemCheckedStateChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="android.view.ActionMode">
+</parameter>
+<parameter name="position" type="int">
+</parameter>
+<parameter name="id" type="long">
+</parameter>
+<parameter name="checked" type="boolean">
+</parameter>
+</method>
+</interface>
<interface name="AbsListView.OnScrollListener"
abstract="true"
static="true"
@@ -222296,19 +222502,6 @@
visibility="public"
>
</method>
-<method name="setAdapter"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="adapter" type="android.widget.ListAdapter">
-</parameter>
-</method>
<method name="setColumnWidth"
return="void"
abstract="false"
@@ -224611,17 +224804,6 @@
<parameter name="v" type="android.view.View">
</parameter>
</method>
-<method name="clearChoices"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="findViewTraversal"
return="android.view.View"
abstract="false"
@@ -224670,61 +224852,6 @@
visibility="public"
>
</method>
-<method name="getCheckedItemCount"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getCheckedItemIds"
- return="long[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getCheckedItemPosition"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getCheckedItemPositions"
- return="android.util.SparseBooleanArray"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getChoiceMode"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="getDivider"
return="android.graphics.drawable.Drawable"
abstract="false"
@@ -224791,19 +224918,6 @@
visibility="public"
>
</method>
-<method name="isItemChecked"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="position" type="int">
-</parameter>
-</method>
<method name="removeFooterView"
return="boolean"
abstract="false"
@@ -224830,32 +224944,6 @@
<parameter name="v" type="android.view.View">
</parameter>
</method>
-<method name="setAdapter"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="adapter" type="android.widget.ListAdapter">
-</parameter>
-</method>
-<method name="setChoiceMode"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="choiceMode" type="int">
-</parameter>
-</method>
<method name="setDivider"
return="void"
abstract="false"
@@ -224908,21 +224996,6 @@
<parameter name="headerDividersEnabled" type="boolean">
</parameter>
</method>
-<method name="setItemChecked"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="position" type="int">
-</parameter>
-<parameter name="value" type="boolean">
-</parameter>
-</method>
<method name="setItemsCanFocus"
return="void"
abstract="false"
@@ -224936,19 +225009,6 @@
<parameter name="itemsCanFocus" type="boolean">
</parameter>
</method>
-<method name="setMultiChoiceModeListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.widget.ListView.MultiChoiceModeListener">
-</parameter>
-</method>
<method name="setSelection"
return="void"
abstract="false"
@@ -225001,50 +225061,6 @@
<parameter name="offset" type="int">
</parameter>
</method>
-<field name="CHOICE_MODE_MULTIPLE"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHOICE_MODE_MULTIPLE_MODAL"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHOICE_MODE_NONE"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHOICE_MODE_SINGLE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
</class>
<class name="ListView.FixedViewInfo"
extends="java.lang.Object"
@@ -225093,35 +225109,6 @@
>
</field>
</class>
-<interface name="ListView.MultiChoiceModeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.view.ActionMode.Callback">
-</implements>
-<method name="onItemCheckedStateChanged"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="mode" type="android.view.ActionMode">
-</parameter>
-<parameter name="position" type="int">
-</parameter>
-<parameter name="id" type="long">
-</parameter>
-<parameter name="checked" type="boolean">
-</parameter>
-</method>
-</interface>
<class name="MediaController"
extends="android.widget.FrameLayout"
abstract="false"
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index e572d3d..c155dda 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -34,10 +34,16 @@
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.SparseBooleanArray;
+import android.view.ActionMode;
+import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -45,7 +51,6 @@
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
-import android.view.ContextMenu.ContextMenuInfo;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -69,6 +74,7 @@
* @attr ref android.R.styleable#AbsListView_cacheColorHint
* @attr ref android.R.styleable#AbsListView_fastScrollEnabled
* @attr ref android.R.styleable#AbsListView_smoothScrollbar
+ * @attr ref android.R.styleable#AbsListView_choiceMode
*/
public abstract class AbsListView extends AdapterView<ListAdapter> implements TextWatcher,
ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,
@@ -167,6 +173,57 @@
static final int LAYOUT_MOVE_SELECTION = 6;
/**
+ * Normal list that does not indicate choices
+ */
+ public static final int CHOICE_MODE_NONE = 0;
+
+ /**
+ * The list allows up to one choice
+ */
+ public static final int CHOICE_MODE_SINGLE = 1;
+
+ /**
+ * The list allows multiple choices
+ */
+ public static final int CHOICE_MODE_MULTIPLE = 2;
+
+ /**
+ * The list allows multiple choices in a modal selection mode
+ */
+ public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;
+
+ /**
+ * Controls if/how the user may choose/check items in the list
+ */
+ int mChoiceMode = CHOICE_MODE_NONE;
+
+ /**
+ * Controls CHOICE_MODE_MULTIPLE_MODAL. null when inactive.
+ */
+ ActionMode mChoiceActionMode;
+
+ /**
+ * Wrapper for the multiple choice mode callback; AbsListView needs to perform
+ * a few extra actions around what application code does.
+ */
+ MultiChoiceModeWrapper mMultiChoiceModeCallback;
+
+ /**
+ * Running count of how many items are currently checked
+ */
+ int mCheckedItemCount;
+
+ /**
+ * Running state of which positions are currently checked
+ */
+ SparseBooleanArray mCheckStates;
+
+ /**
+ * Running state of which IDs are currently checked
+ */
+ LongSparseArray<Boolean> mCheckedIdStates;
+
+ /**
* Controls how the next layout will happen
*/
int mLayoutMode = LAYOUT_NORMAL;
@@ -577,6 +634,8 @@
});
}
+ setChoiceMode(a.getInt(R.styleable.AbsListView_choiceMode, CHOICE_MODE_NONE));
+
a.recycle();
}
@@ -596,6 +655,311 @@
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+ if (adapter != null) {
+ if (mChoiceMode != CHOICE_MODE_NONE && mAdapter.hasStableIds() &&
+ mCheckedIdStates == null) {
+ mCheckedIdStates = new LongSparseArray<Boolean>();
+ }
+ }
+
+ if (mCheckStates != null) {
+ mCheckStates.clear();
+ }
+
+ if (mCheckedIdStates != null) {
+ mCheckedIdStates.clear();
+ }
+ }
+
+ /**
+ * Returns the number of items currently selected. This will only be valid
+ * if the choice mode is not {@link #CHOICE_MODE_NONE} (default).
+ *
+ * <p>To determine the specific items that are currently selected, use one of
+ * the <code>getChecked*</code> methods.
+ *
+ * @return The number of items currently selected
+ *
+ * @see #getCheckedItemPosition()
+ * @see #getCheckedItemPositions()
+ * @see #getCheckedItemIds()
+ */
+ public int getCheckedItemCount() {
+ return mCheckedItemCount;
+ }
+
+ /**
+ * Returns the checked state of the specified position. The result is only
+ * valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE}
+ * or {@link #CHOICE_MODE_MULTIPLE}.
+ *
+ * @param position The item whose checked state to return
+ * @return The item's checked state or <code>false</code> if choice mode
+ * is invalid
+ *
+ * @see #setChoiceMode(int)
+ */
+ public boolean isItemChecked(int position) {
+ if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
+ return mCheckStates.get(position);
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the currently checked item. The result is only valid if the choice
+ * mode has been set to {@link #CHOICE_MODE_SINGLE}.
+ *
+ * @return The position of the currently checked item or
+ * {@link #INVALID_POSITION} if nothing is selected
+ *
+ * @see #setChoiceMode(int)
+ */
+ public int getCheckedItemPosition() {
+ if (mChoiceMode == CHOICE_MODE_SINGLE && mCheckStates != null && mCheckStates.size() == 1) {
+ return mCheckStates.keyAt(0);
+ }
+
+ return INVALID_POSITION;
+ }
+
+ /**
+ * Returns the set of checked items in the list. The result is only valid if
+ * the choice mode has not been set to {@link #CHOICE_MODE_NONE}.
+ *
+ * @return A SparseBooleanArray which will return true for each call to
+ * get(int position) where position is a position in the list,
+ * or <code>null</code> if the choice mode is set to
+ * {@link #CHOICE_MODE_NONE}.
+ */
+ public SparseBooleanArray getCheckedItemPositions() {
+ if (mChoiceMode != CHOICE_MODE_NONE) {
+ return mCheckStates;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the set of checked items ids. The result is only valid if the
+ * choice mode has not been set to {@link #CHOICE_MODE_NONE} and the adapter
+ * has stable IDs. ({@link ListAdapter#hasStableIds()} == {@code true})
+ *
+ * @return A new array which contains the id of each checked item in the
+ * list.
+ */
+ public long[] getCheckedItemIds() {
+ if (mChoiceMode == CHOICE_MODE_NONE || mCheckedIdStates == null || mAdapter == null) {
+ return new long[0];
+ }
+
+ final LongSparseArray<Boolean> idStates = mCheckedIdStates;
+ final int count = idStates.size();
+ final long[] ids = new long[count];
+
+ for (int i = 0; i < count; i++) {
+ ids[i] = idStates.keyAt(i);
+ }
+
+ return ids;
+ }
+
+ /**
+ * Clear any choices previously set
+ */
+ public void clearChoices() {
+ if (mCheckStates != null) {
+ mCheckStates.clear();
+ }
+ if (mCheckedIdStates != null) {
+ mCheckedIdStates.clear();
+ }
+ mCheckedItemCount = 0;
+ }
+
+ /**
+ * Sets the checked state of the specified position. The is only valid if
+ * the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or
+ * {@link #CHOICE_MODE_MULTIPLE}.
+ *
+ * @param position The item whose checked state is to be checked
+ * @param value The new checked state for the item
+ */
+ public void setItemChecked(int position, boolean value) {
+ if (mChoiceMode == CHOICE_MODE_NONE) {
+ return;
+ }
+
+ // Start selection mode if needed. We don't need to if we're unchecking something.
+ if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
+ mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
+ }
+
+ if (mChoiceMode == CHOICE_MODE_MULTIPLE || mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
+ boolean oldValue = mCheckStates.get(position);
+ mCheckStates.put(position, value);
+ if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
+ if (value) {
+ mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+ } else {
+ mCheckedIdStates.delete(mAdapter.getItemId(position));
+ }
+ }
+ if (oldValue != value) {
+ if (value) {
+ mCheckedItemCount++;
+ } else {
+ mCheckedItemCount--;
+ }
+ }
+ if (mChoiceActionMode != null) {
+ final long id = mAdapter.getItemId(position);
+ mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
+ position, id, value);
+ }
+ } else {
+ boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds();
+ // Clear all values if we're checking something, or unchecking the currently
+ // selected item
+ if (value || isItemChecked(position)) {
+ mCheckStates.clear();
+ if (updateIds) {
+ mCheckedIdStates.clear();
+ }
+ }
+ // this may end up selecting the value we just cleared but this way
+ // we ensure length of mCheckStates is 1, a fact getCheckedItemPosition relies on
+ if (value) {
+ mCheckStates.put(position, true);
+ if (updateIds) {
+ mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+ }
+ mCheckedItemCount = 1;
+ } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
+ mCheckedItemCount = 0;
+ }
+ }
+
+ // Do not generate a data change while we are in the layout phase
+ if (!mInLayout && !mBlockLayoutRequests) {
+ mDataChanged = true;
+ rememberSyncState();
+ requestLayout();
+ }
+ }
+
+ @Override
+ public boolean performItemClick(View view, int position, long id) {
+ boolean handled = false;
+
+ if (mChoiceMode != CHOICE_MODE_NONE) {
+ handled = true;
+
+ if (mChoiceMode == CHOICE_MODE_MULTIPLE ||
+ (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode != null)) {
+ boolean newValue = !mCheckStates.get(position, false);
+ mCheckStates.put(position, newValue);
+ if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
+ if (newValue) {
+ mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+ } else {
+ mCheckedIdStates.delete(mAdapter.getItemId(position));
+ }
+ }
+ if (newValue) {
+ mCheckedItemCount++;
+ } else {
+ mCheckedItemCount--;
+ }
+ if (mChoiceActionMode != null) {
+ mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
+ position, id, newValue);
+ }
+ } else if (mChoiceMode == CHOICE_MODE_SINGLE) {
+ boolean newValue = !mCheckStates.get(position, false);
+ if (newValue) {
+ mCheckStates.clear();
+ mCheckStates.put(position, true);
+ if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
+ mCheckedIdStates.clear();
+ mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+ }
+ mCheckedItemCount = 1;
+ } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
+ mCheckedItemCount = 0;
+ }
+ }
+
+ mDataChanged = true;
+ rememberSyncState();
+ requestLayout();
+ }
+
+ handled |= super.performItemClick(view, position, id);
+
+ return handled;
+ }
+
+ /**
+ * @see #setChoiceMode(int)
+ *
+ * @return The current choice mode
+ */
+ public int getChoiceMode() {
+ return mChoiceMode;
+ }
+
+ /**
+ * Defines the choice behavior for the List. By default, Lists do not have any choice behavior
+ * ({@link #CHOICE_MODE_NONE}). By setting the choiceMode to {@link #CHOICE_MODE_SINGLE}, the
+ * List allows up to one item to be in a chosen state. By setting the choiceMode to
+ * {@link #CHOICE_MODE_MULTIPLE}, the list allows any number of items to be chosen.
+ *
+ * @param choiceMode One of {@link #CHOICE_MODE_NONE}, {@link #CHOICE_MODE_SINGLE}, or
+ * {@link #CHOICE_MODE_MULTIPLE}
+ */
+ public void setChoiceMode(int choiceMode) {
+ mChoiceMode = choiceMode;
+ if (mChoiceActionMode != null) {
+ mChoiceActionMode.finish();
+ mChoiceActionMode = null;
+ }
+ if (mChoiceMode != CHOICE_MODE_NONE) {
+ if (mCheckStates == null) {
+ mCheckStates = new SparseBooleanArray();
+ }
+ if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) {
+ mCheckedIdStates = new LongSparseArray<Boolean>();
+ }
+ // Modal multi-choice mode only has choices when the mode is active. Clear them.
+ if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
+ clearChoices();
+ setLongClickable(true);
+ }
+ }
+ }
+
+ /**
+ * Set a {@link MultiChoiceModeListener} that will manage the lifecycle of the
+ * selection {@link ActionMode}. Only used when the choice mode is set to
+ * {@link #CHOICE_MODE_MULTIPLE_MODAL}.
+ *
+ * @param listener Listener that will manage the selection mode
+ *
+ * @see #setChoiceMode(int)
+ */
+ public void setMultiChoiceModeListener(MultiChoiceModeListener listener) {
+ if (mMultiChoiceModeCallback == null) {
+ mMultiChoiceModeCallback = new MultiChoiceModeWrapper();
+ }
+ mMultiChoiceModeCallback.setWrapped(listener);
+ }
+
+ /**
* Enables fast scrolling by letting the user quickly scroll through lists by
* dragging the fast scroll thumb. The adapter attached to the list may want
* to implement {@link SectionIndexer} if it wishes to display alphabet preview and
@@ -813,6 +1177,8 @@
int position;
int height;
String filter;
+ SparseBooleanArray checkState;
+ LongSparseArray<Boolean> checkIdState;
/**
* Constructor called from {@link AbsListView#onSaveInstanceState()}
@@ -832,6 +1198,13 @@
position = in.readInt();
height = in.readInt();
filter = in.readString();
+ checkState = in.readSparseBooleanArray();
+ long[] idState = in.createLongArray();
+
+ if (idState.length > 0) {
+ checkIdState = new LongSparseArray<Boolean>();
+ checkIdState.setValues(idState, Boolean.TRUE);
+ }
}
@Override
@@ -843,6 +1216,8 @@
out.writeInt(position);
out.writeInt(height);
out.writeString(filter);
+ out.writeSparseBooleanArray(checkState);
+ out.writeLongArray(checkIdState != null ? checkIdState.getKeys() : new long[0]);
}
@Override
@@ -854,7 +1229,8 @@
+ " viewTop=" + viewTop
+ " position=" + position
+ " height=" + height
- + " filter=" + filter + "}";
+ + " filter=" + filter
+ + " checkState=" + checkState + "}";
}
public static final Parcelable.Creator<SavedState> CREATOR
@@ -918,6 +1294,9 @@
}
}
+ ss.checkState = mCheckStates;
+ ss.checkIdState = mCheckedIdStates;
+
return ss;
}
@@ -949,6 +1328,14 @@
setFilterText(ss.filter);
+ if (ss.checkState != null) {
+ mCheckStates = ss.checkState;
+ }
+
+ if (ss.checkIdState != null) {
+ mCheckedIdStates = ss.checkIdState;
+ }
+
requestLayout();
}
@@ -1774,8 +2161,19 @@
boolean performLongPress(final View child,
final int longPressPosition, final long longPressId) {
- boolean handled = false;
+ // CHOICE_MODE_MULTIPLE_MODAL takes over long press.
+ if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
+ if (mChoiceActionMode == null) {
+ mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
+ setItemChecked(longPressPosition, true);
+ }
+ // TODO Should we select the long pressed item if we were already in
+ // selection mode? (i.e. treat it like an item click?)
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ return true;
+ }
+ boolean handled = false;
if (mOnItemLongClickListener != null) {
handled = mOnItemLongClickListener.onItemLongClick(AbsListView.this, child,
longPressPosition, longPressId);
@@ -3991,6 +4389,75 @@
}
/**
+ * A MultiChoiceModeListener receives events for {@link AbsListView#CHOICE_MODE_MULTIPLE_MODAL}.
+ * It acts as the {@link ActionMode.Callback} for the selection mode and also receives
+ * {@link #onItemCheckedStateChanged(ActionMode, int, long, boolean)} events when the user
+ * selects and deselects list items.
+ */
+ public interface MultiChoiceModeListener extends ActionMode.Callback {
+ /**
+ * Called when an item is checked or unchecked during selection mode.
+ *
+ * @param mode The {@link ActionMode} providing the selection mode
+ * @param position Adapter position of the item that was checked or unchecked
+ * @param id Adapter ID of the item that was checked or unchecked
+ * @param checked <code>true</code> if the item is now checked, <code>false</code>
+ * if the item is now unchecked.
+ */
+ public void onItemCheckedStateChanged(ActionMode mode,
+ int position, long id, boolean checked);
+ }
+
+ class MultiChoiceModeWrapper implements MultiChoiceModeListener {
+ private MultiChoiceModeListener mWrapped;
+
+ public void setWrapped(MultiChoiceModeListener wrapped) {
+ mWrapped = wrapped;
+ }
+
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ if (mWrapped.onCreateActionMode(mode, menu)) {
+ // Initialize checked graphic state?
+ setLongClickable(false);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return mWrapped.onPrepareActionMode(mode, menu);
+ }
+
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return mWrapped.onActionItemClicked(mode, item);
+ }
+
+ public void onDestroyActionMode(ActionMode mode) {
+ mWrapped.onDestroyActionMode(mode);
+ mChoiceActionMode = null;
+
+ // Ending selection mode means deselecting everything.
+ clearChoices();
+
+ mDataChanged = true;
+ rememberSyncState();
+ requestLayout();
+
+ setLongClickable(true);
+ }
+
+ 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 extends LayoutParams to provide a place to hold the view type.
*/
public static class LayoutParams extends ViewGroup.LayoutParams {
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index a5b3ed5..ea767f6 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -104,6 +104,26 @@
a.recycle();
}
+ /**
+ * Set how the user may select items from the grid.
+ *
+ * <p>GridView only supports {@link AbsListView#CHOICE_MODE_NONE} and
+ * {@link AbsListView#CHOICE_MODE_MULTIPLE_MODAL}. Attempting to set an unsupported choice
+ * mode will throw an UnsupportedOperationException.
+ */
+ @Override
+ public void setChoiceMode(int choiceMode) {
+ switch (choiceMode) {
+ case CHOICE_MODE_NONE:
+ case CHOICE_MODE_MULTIPLE_MODAL:
+ super.setChoiceMode(choiceMode);
+ break;
+
+ default:
+ throw new UnsupportedOperationException("Unsupported choice mode " + choiceMode);
+ }
+ }
+
@Override
public ListAdapter getAdapter() {
return mAdapter;
@@ -136,7 +156,10 @@
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
-
+
+ // AbsListView#setAdapter will update choice mode states.
+ super.setAdapter(adapter);
+
if (mAdapter != null) {
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
@@ -1312,6 +1335,12 @@
child.setPressed(isPressed);
}
+ if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
+ if (child instanceof Checkable) {
+ ((Checkable) child).setChecked(mCheckStates.get(position));
+ }
+ }
+
if (needToMeasure) {
int childHeightSpec = ViewGroup.getChildMeasureSpec(
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height);
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 0d0a1ba..de7157b 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -28,17 +28,10 @@
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.util.AttributeSet;
-import android.util.LongSparseArray;
import android.util.SparseBooleanArray;
-import android.view.ActionMode;
import android.view.FocusFinder;
-import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
@@ -67,7 +60,6 @@
* @attr ref android.R.styleable#ListView_entries
* @attr ref android.R.styleable#ListView_divider
* @attr ref android.R.styleable#ListView_dividerHeight
- * @attr ref android.R.styleable#ListView_choiceMode
* @attr ref android.R.styleable#ListView_headerDividersEnabled
* @attr ref android.R.styleable#ListView_footerDividersEnabled
*/
@@ -79,26 +71,6 @@
static final int NO_POSITION = -1;
/**
- * Normal list that does not indicate choices
- */
- public static final int CHOICE_MODE_NONE = 0;
-
- /**
- * The list allows up to one choice
- */
- public static final int CHOICE_MODE_SINGLE = 1;
-
- /**
- * The list allows multiple choices
- */
- public static final int CHOICE_MODE_MULTIPLE = 2;
-
- /**
- * The list allows multiple choices in a modal selection mode
- */
- public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;
-
- /**
* When arrow scrolling, ListView will never scroll more than this factor
* times the height of the list.
*/
@@ -141,11 +113,6 @@
private boolean mItemsCanFocus = false;
- private int mChoiceMode = CHOICE_MODE_NONE;
-
- private SparseBooleanArray mCheckStates;
- private LongSparseArray<Boolean> mCheckedIdStates;
-
// used for temporary calculations.
private final Rect mTempRect = new Rect();
private Paint mDividerPaint;
@@ -157,11 +124,6 @@
// Keeps focused children visible through resizes
private FocusSelector mFocusSelector;
- // Controls CHOICE_MODE_MULTIPLE_MODAL. null when inactive.
- private ActionMode mChoiceActionMode;
- private MultiChoiceModeWrapper mMultiChoiceModeCallback;
- private int mCheckedItemCount;
-
public ListView(Context context) {
this(context, null);
}
@@ -196,8 +158,6 @@
setDividerHeight(dividerHeight);
}
- setChoiceMode(a.getInt(R.styleable.ListView_choiceMode, CHOICE_MODE_NONE));
-
mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, true);
mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, true);
@@ -457,6 +417,10 @@
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
+
+ // AbsListView#setAdapter will update choice mode states.
+ super.setAdapter(adapter);
+
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
@@ -481,13 +445,6 @@
// Nothing selected
checkSelectionChanged();
}
-
- if (mChoiceMode != CHOICE_MODE_NONE &&
- mAdapter.hasStableIds() &&
- mCheckedIdStates == null) {
- mCheckedIdStates = new LongSparseArray<Boolean>();
- }
-
} else {
mAreAllItemsSelectable = true;
checkFocus();
@@ -495,14 +452,6 @@
checkSelectionChanged();
}
- if (mCheckStates != null) {
- mCheckStates.clear();
- }
-
- if (mCheckedIdStates != null) {
- mCheckedIdStates.clear();
- }
-
requestLayout();
}
@@ -3360,270 +3309,6 @@
}
/**
- * @see #setChoiceMode(int)
- *
- * @return The current choice mode
- */
- public int getChoiceMode() {
- return mChoiceMode;
- }
-
- /**
- * Defines the choice behavior for the List. By default, Lists do not have any choice behavior
- * ({@link #CHOICE_MODE_NONE}). By setting the choiceMode to {@link #CHOICE_MODE_SINGLE}, the
- * List allows up to one item to be in a chosen state. By setting the choiceMode to
- * {@link #CHOICE_MODE_MULTIPLE}, the list allows any number of items to be chosen.
- *
- * @param choiceMode One of {@link #CHOICE_MODE_NONE}, {@link #CHOICE_MODE_SINGLE}, or
- * {@link #CHOICE_MODE_MULTIPLE}
- */
- public void setChoiceMode(int choiceMode) {
- mChoiceMode = choiceMode;
- if (mChoiceActionMode != null) {
- mChoiceActionMode.finish();
- mChoiceActionMode = null;
- }
- if (mChoiceMode != CHOICE_MODE_NONE) {
- if (mCheckStates == null) {
- mCheckStates = new SparseBooleanArray();
- }
- if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) {
- mCheckedIdStates = new LongSparseArray<Boolean>();
- }
- // Modal multi-choice mode only has choices when the mode is active. Clear them.
- if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
- clearChoices();
- setLongClickable(true);
- }
- }
- }
-
- /**
- * Set a {@link MultiChoiceModeListener} that will manage the lifecycle of the
- * selection {@link ActionMode}. Only used when the choice mode is set to
- * {@link #CHOICE_MODE_MULTIPLE_MODAL}.
- *
- * @param listener Listener that will manage the selection mode
- *
- * @see #setChoiceMode(int)
- */
- public void setMultiChoiceModeListener(MultiChoiceModeListener listener) {
- if (mMultiChoiceModeCallback == null) {
- mMultiChoiceModeCallback = new MultiChoiceModeWrapper();
- }
- mMultiChoiceModeCallback.setWrapped(listener);
- }
-
- @Override
- boolean performLongPress(final View child,
- final int longPressPosition, final long longPressId) {
- boolean handled = false;
- if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
- handled = true;
- if (mChoiceActionMode == null) {
- mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
- setItemChecked(longPressPosition, true);
- }
- // TODO Should we select the long pressed item if we were already in
- // selection mode? (i.e. treat it like an item click?)
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- }
- return handled | super.performLongPress(child, longPressPosition, longPressId);
- }
-
- @Override
- public boolean performItemClick(View view, int position, long id) {
- boolean handled = false;
-
- if (mChoiceMode != CHOICE_MODE_NONE) {
- handled = true;
-
- if (mChoiceMode == CHOICE_MODE_MULTIPLE ||
- (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode != null)) {
- boolean newValue = !mCheckStates.get(position, false);
- mCheckStates.put(position, newValue);
- if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
- if (newValue) {
- mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
- } else {
- mCheckedIdStates.delete(mAdapter.getItemId(position));
- }
- }
- if (newValue) {
- mCheckedItemCount++;
- } else {
- mCheckedItemCount--;
- }
- if (mChoiceActionMode != null) {
- mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
- position, id, newValue);
- }
- } else if (mChoiceMode == CHOICE_MODE_SINGLE) {
- boolean newValue = !mCheckStates.get(position, false);
- if (newValue) {
- mCheckStates.clear();
- mCheckStates.put(position, true);
- if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
- mCheckedIdStates.clear();
- mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
- }
- mCheckedItemCount = 1;
- } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
- mCheckedItemCount = 0;
- }
- }
-
- mDataChanged = true;
- rememberSyncState();
- requestLayout();
- }
-
- handled |= super.performItemClick(view, position, id);
-
- return handled;
- }
-
- /**
- * Sets the checked state of the specified position. The is only valid if
- * the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or
- * {@link #CHOICE_MODE_MULTIPLE}.
- *
- * @param position The item whose checked state is to be checked
- * @param value The new checked state for the item
- */
- public void setItemChecked(int position, boolean value) {
- if (mChoiceMode == CHOICE_MODE_NONE) {
- return;
- }
-
- // Start selection mode if needed. We don't need to if we're unchecking something.
- if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
- mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
- }
-
- if (mChoiceMode == CHOICE_MODE_MULTIPLE || mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
- boolean oldValue = mCheckStates.get(position);
- mCheckStates.put(position, value);
- if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
- if (value) {
- mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
- } else {
- mCheckedIdStates.delete(mAdapter.getItemId(position));
- }
- }
- if (oldValue != value) {
- if (value) {
- mCheckedItemCount++;
- } else {
- mCheckedItemCount--;
- }
- }
- if (mChoiceActionMode != null) {
- final long id = mAdapter.getItemId(position);
- mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
- position, id, value);
- }
- } else {
- boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds();
- // Clear all values if we're checking something, or unchecking the currently
- // selected item
- if (value || isItemChecked(position)) {
- mCheckStates.clear();
- if (updateIds) {
- mCheckedIdStates.clear();
- }
- }
- // this may end up selecting the value we just cleared but this way
- // we ensure length of mCheckStates is 1, a fact getCheckedItemPosition relies on
- if (value) {
- mCheckStates.put(position, true);
- if (updateIds) {
- mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
- }
- mCheckedItemCount = 1;
- } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
- mCheckedItemCount = 0;
- }
- }
-
- // Do not generate a data change while we are in the layout phase
- if (!mInLayout && !mBlockLayoutRequests) {
- mDataChanged = true;
- rememberSyncState();
- requestLayout();
- }
- }
-
- /**
- * Returns the number of items currently selected. This will only be valid
- * if the choice mode is not {@link #CHOICE_MODE_NONE} (default).
- *
- * <p>To determine the specific items that are currently selected, use one of
- * the <code>getChecked*</code> methods.
- *
- * @return The number of items currently selected
- *
- * @see #getCheckedItemPosition()
- * @see #getCheckedItemPositions()
- * @see #getCheckedItemIds()
- */
- public int getCheckedItemCount() {
- return mCheckedItemCount;
- }
-
- /**
- * Returns the checked state of the specified position. The result is only
- * valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE}
- * or {@link #CHOICE_MODE_MULTIPLE}.
- *
- * @param position The item whose checked state to return
- * @return The item's checked state or <code>false</code> if choice mode
- * is invalid
- *
- * @see #setChoiceMode(int)
- */
- public boolean isItemChecked(int position) {
- if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
- return mCheckStates.get(position);
- }
-
- return false;
- }
-
- /**
- * Returns the currently checked item. The result is only valid if the choice
- * mode has been set to {@link #CHOICE_MODE_SINGLE}.
- *
- * @return The position of the currently checked item or
- * {@link #INVALID_POSITION} if nothing is selected
- *
- * @see #setChoiceMode(int)
- */
- public int getCheckedItemPosition() {
- if (mChoiceMode == CHOICE_MODE_SINGLE && mCheckStates != null && mCheckStates.size() == 1) {
- return mCheckStates.keyAt(0);
- }
-
- return INVALID_POSITION;
- }
-
- /**
- * Returns the set of checked items in the list. The result is only valid if
- * the choice mode has not been set to {@link #CHOICE_MODE_NONE}.
- *
- * @return A SparseBooleanArray which will return true for each call to
- * get(int position) where position is a position in the list,
- * or <code>null</code> if the choice mode is set to
- * {@link #CHOICE_MODE_NONE}.
- */
- public SparseBooleanArray getCheckedItemPositions() {
- if (mChoiceMode != CHOICE_MODE_NONE) {
- return mCheckStates;
- }
- return null;
- }
-
- /**
* Returns the set of checked items ids. The result is only valid if the
* choice mode has not been set to {@link #CHOICE_MODE_NONE}.
*
@@ -3667,185 +3352,4 @@
}
return new long[0];
}
-
- /**
- * Returns the set of checked items ids. The result is only valid if the
- * choice mode has not been set to {@link #CHOICE_MODE_NONE} and the adapter
- * has stable IDs. ({@link ListAdapter#hasStableIds()} == {@code true})
- *
- * @return A new array which contains the id of each checked item in the
- * list.
- */
- public long[] getCheckedItemIds() {
- if (mChoiceMode == CHOICE_MODE_NONE || mCheckedIdStates == null || mAdapter == null) {
- return new long[0];
- }
-
- final LongSparseArray<Boolean> idStates = mCheckedIdStates;
- final int count = idStates.size();
- final long[] ids = new long[count];
-
- for (int i = 0; i < count; i++) {
- ids[i] = idStates.keyAt(i);
- }
-
- return ids;
- }
-
- /**
- * Clear any choices previously set
- */
- public void clearChoices() {
- if (mCheckStates != null) {
- mCheckStates.clear();
- }
- if (mCheckedIdStates != null) {
- mCheckedIdStates.clear();
- }
- mCheckedItemCount = 0;
- }
-
- /**
- * A MultiChoiceModeListener receives events for {@link ListView#CHOICE_MODE_MULTIPLE_MODAL}.
- * It acts as the {@link ActionMode.Callback} for the selection mode and also receives
- * {@link #onItemCheckedStateChanged(ActionMode, int, long, boolean)} events when the user
- * selects and deselects list items.
- */
- public interface MultiChoiceModeListener extends ActionMode.Callback {
- /**
- * Called when an item is checked or unchecked during selection mode.
- *
- * @param mode The {@link ActionMode} providing the selection mode
- * @param position Adapter position of the item that was checked or unchecked
- * @param id Adapter ID of the item that was checked or unchecked
- * @param checked <code>true</code> if the item is now checked, <code>false</code>
- * if the item is now unchecked.
- */
- public void onItemCheckedStateChanged(ActionMode mode,
- int position, long id, boolean checked);
- }
-
- private class MultiChoiceModeWrapper implements MultiChoiceModeListener {
- private MultiChoiceModeListener mWrapped;
-
- public void setWrapped(MultiChoiceModeListener wrapped) {
- mWrapped = wrapped;
- }
-
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- if (mWrapped.onCreateActionMode(mode, menu)) {
- // Initialize checked graphic state?
- setLongClickable(false);
- return true;
- }
- return false;
- }
-
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return mWrapped.onPrepareActionMode(mode, menu);
- }
-
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- return mWrapped.onActionItemClicked(mode, item);
- }
-
- public void onDestroyActionMode(ActionMode mode) {
- mWrapped.onDestroyActionMode(mode);
- mChoiceActionMode = null;
-
- // Ending selection mode means deselecting everything.
- clearChoices();
-
- mDataChanged = true;
- rememberSyncState();
- requestLayout();
-
- setLongClickable(true);
- }
-
- 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();
- }
- }
- }
-
- static class SavedState extends BaseSavedState {
- SparseBooleanArray checkState;
- LongSparseArray<Boolean> checkIdState;
-
- /**
- * Constructor called from {@link ListView#onSaveInstanceState()}
- */
- SavedState(Parcelable superState, SparseBooleanArray checkState,
- LongSparseArray<Boolean> checkIdState) {
- super(superState);
- this.checkState = checkState;
- this.checkIdState = checkIdState;
- }
-
- /**
- * Constructor called from {@link #CREATOR}
- */
- private SavedState(Parcel in) {
- super(in);
- checkState = in.readSparseBooleanArray();
- long[] idState = in.createLongArray();
-
- if (idState.length > 0) {
- checkIdState = new LongSparseArray<Boolean>();
- checkIdState.setValues(idState, Boolean.TRUE);
- }
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- super.writeToParcel(out, flags);
- out.writeSparseBooleanArray(checkState);
- out.writeLongArray(checkIdState != null ? checkIdState.getKeys() : new long[0]);
- }
-
- @Override
- public String toString() {
- return "ListView.SavedState{"
- + Integer.toHexString(System.identityHashCode(this))
- + " checkState=" + checkState + "}";
- }
-
- public static final Parcelable.Creator<SavedState> CREATOR
- = new Parcelable.Creator<SavedState>() {
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- Parcelable superState = super.onSaveInstanceState();
- return new SavedState(superState, mCheckStates, mCheckedIdStates);
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- SavedState ss = (SavedState) state;
-
- super.onRestoreInstanceState(ss.getSuperState());
-
- if (ss.checkState != null) {
- mCheckStates = ss.checkState;
- }
-
- if (ss.checkIdState != null) {
- mCheckedIdStates = ss.checkIdState;
- }
- }
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e0608f9..67327b2 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1594,6 +1594,26 @@
<attr name="smoothScrollbar" format="boolean" />
<!-- A reference to an XML description of the adapter to attach to the list. -->
<attr name="adapter" format="reference" />
+ <!-- Defines the choice behavior for the view. By default, lists do not have
+ any choice behavior. By setting the choiceMode to singleChoice, the list
+ allows up to one item to be in a chosen state. By setting the choiceMode to
+ multipleChoice, the list allows any number of items to be chosen.
+ Finally, by setting the choiceMode to multipleChoiceModal the list allows
+ any number of items to be chosen in a special selection mode.
+ The application will supply a
+ {@link android.widget.AbsListView.MultiChoiceModeListener} using
+ {@link android.widget.AbsListView#setMultiChoiceModeListener} to control the
+ selection mode. This uses the {@link android.view.ActionMode} API. -->
+ <attr name="choiceMode">
+ <!-- Normal list that does not indicate choices. -->
+ <enum name="none" value="0" />
+ <!-- The list allows up to one choice. -->
+ <enum name="singleChoice" value="1" />
+ <!-- The list allows multiple choices. -->
+ <enum name="multipleChoice" value="2" />
+ <!-- The list allows multiple choices in a custom selection mode. -->
+ <enum name="multipleChoiceModal" value="3" />
+ </attr>
</declare-styleable>
<declare-styleable name="AbsSpinner">
<!-- Reference to an array resource that will populate the Spinner. For static content,
@@ -1800,20 +1820,6 @@
<!-- Height of the divider. Will use the intrinsic height of the divider if this
is not specified. -->
<attr name="dividerHeight" format="dimension" />
- <!-- Defines the choice behavior for the ListView. By default, lists do not have
- any choice behavior. By setting the choiceMode to singleChoice, the List
- allows up to one item to be in a chosen state. By setting the choiceMode to
- multipleChoice, the list allows any number of items to be chosen. -->
- <attr name="choiceMode">
- <!-- Normal list that does not indicate choices. -->
- <enum name="none" value="0" />
- <!-- The list allows up to one choice. -->
- <enum name="singleChoice" value="1" />
- <!-- The list allows multiple choices. -->
- <enum name="multipleChoice" value="2" />
- <!-- The list allows multiple choices in a custom selection mode. -->
- <enum name="multipleChoiceModal" value="3" />
- </attr>
<!-- When set to false, the ListView will not draw the divider after each header view.
The default value is true. -->
<attr name="headerDividersEnabled" format="boolean" />