Merge "Added CHOICE_MODE_MULTIPLE_MODAL to ListView."
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -89,6 +93,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3464,6 +3554,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}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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. -->
|
||||
|
||||
Reference in New Issue
Block a user