diff options
| author | Adam Powell <adamp@google.com> | 2010-07-28 14:27:28 -0700 |
|---|---|---|
| committer | Adam Powell <adamp@google.com> | 2010-08-02 11:28:31 -0700 |
| commit | 8350f7dbc3a62211b2891f35911e4073d24c4cc5 (patch) | |
| tree | ded2cb4b1c028db8e861e3d3a5890ca6884308c4 | |
| parent | 08364d867e6df7d95a20f57d0012699ff7560bce (diff) | |
| download | frameworks_base-8350f7dbc3a62211b2891f35911e4073d24c4cc5.zip frameworks_base-8350f7dbc3a62211b2891f35911e4073d24c4cc5.tar.gz frameworks_base-8350f7dbc3a62211b2891f35911e4073d24c4cc5.tar.bz2 | |
Added CHOICE_MODE_MULTIPLE_MODAL to ListView.
Based on ActionModes, CHOICE_MODE_MULTIPLE_MODAL supports the new
Honeycomb-style selection mode.
Change-Id: I293ea22487db8e081c95e5236f1e053f7dd4ada6
| -rw-r--r-- | api/current.xml | 64 | ||||
| -rw-r--r-- | core/java/android/widget/AbsListView.java | 2 | ||||
| -rw-r--r-- | core/java/android/widget/ListView.java | 188 | ||||
| -rw-r--r-- | core/java/com/android/internal/widget/ActionBarContextView.java | 2 | ||||
| -rwxr-xr-x | core/res/res/values/attrs.xml | 2 |
5 files changed, 252 insertions, 6 deletions
diff --git a/api/current.xml b/api/current.xml index 276c64c..942b120 100644 --- a/api/current.xml +++ b/api/current.xml @@ -219371,6 +219371,17 @@ 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" @@ -219626,6 +219637,19 @@ <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" @@ -219689,6 +219713,17 @@ 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" @@ -219759,6 +219794,35 @@ > </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 1658b2f..372cc83 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -1772,7 +1772,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } - private boolean performLongPress(final View child, + boolean performLongPress(final View child, final int longPressPosition, final long longPressId) { boolean handled = false; diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index ad9d930..0bb41e5 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -33,8 +33,12 @@ 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; @@ -90,6 +94,11 @@ public class ListView extends AbsListView { 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. */ @@ -147,7 +156,12 @@ public class ListView extends AbsListView { // 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); } @@ -3365,6 +3379,10 @@ public class ListView extends AbsListView { */ 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(); @@ -3372,9 +3390,47 @@ public class ListView extends AbsListView { 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; @@ -3382,7 +3438,8 @@ public class ListView extends AbsListView { if (mChoiceMode != CHOICE_MODE_NONE) { handled = true; - if (mChoiceMode == CHOICE_MODE_MULTIPLE) { + 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()) { @@ -3392,7 +3449,16 @@ public class ListView extends AbsListView { mCheckedIdStates.delete(mAdapter.getItemId(position)); } } - } else { + 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(); @@ -3401,7 +3467,10 @@ public class ListView extends AbsListView { mCheckedIdStates.clear(); mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE); } - } + mCheckedItemCount = 1; + } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) { + mCheckedItemCount = 0; + } } mDataChanged = true; @@ -3427,7 +3496,13 @@ public class ListView extends AbsListView { return; } - if (mChoiceMode == CHOICE_MODE_MULTIPLE) { + // 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) { @@ -3436,6 +3511,18 @@ public class ListView extends AbsListView { 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 @@ -3453,6 +3540,9 @@ public class ListView extends AbsListView { if (updateIds) { mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE); } + mCheckedItemCount = 1; + } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) { + mCheckedItemCount = 0; } } @@ -3465,6 +3555,23 @@ public class ListView extends AbsListView { } /** + * 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}. @@ -3525,6 +3632,7 @@ public class ListView extends AbsListView { * * @deprecated Use {@link #getCheckedItemIds()} instead. */ + @Deprecated public long[] getCheckItemIds() { // Use new behavior that correctly handles stable ID mapping. if (mAdapter != null && mAdapter.hasStableIds()) { @@ -3594,6 +3702,76 @@ public class ListView extends AbsListView { 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 { diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index 0bedc4e..5518b3e 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -124,11 +124,13 @@ public class ActionBarContextView extends ViewGroup { } if (mSubtitle != null) { mSubtitleView.setText(mSubtitle); + mSubtitleView.setVisibility(VISIBLE); } addView(mTitleLayout); } else { mTitleView.setText(mTitle); mSubtitleView.setText(mSubtitle); + mSubtitleView.setVisibility(mSubtitle != null ? VISIBLE : GONE); if (mTitleLayout.getParent() == null) { addView(mTitleLayout); } diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 6c14a3b..974733f 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1790,6 +1790,8 @@ <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. --> |
