summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
authorAdam Powell <adamp@google.com>2010-07-28 14:27:28 -0700
committerAdam Powell <adamp@google.com>2010-08-02 11:28:31 -0700
commit8350f7dbc3a62211b2891f35911e4073d24c4cc5 (patch)
treeded2cb4b1c028db8e861e3d3a5890ca6884308c4 /core/java
parent08364d867e6df7d95a20f57d0012699ff7560bce (diff)
downloadframeworks_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
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/widget/AbsListView.java2
-rw-r--r--core/java/android/widget/ListView.java188
-rw-r--r--core/java/com/android/internal/widget/ActionBarContextView.java2
3 files changed, 186 insertions, 6 deletions
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);
}