Use MultiSelectionUtil from Android Samples for CHOICE_MODE_MULTIPLE_MODAL

Fixed:
- Background highlighting

Broken:
- Selecting emails by clicking pictures
- Tick appearing over picture of selected emails
This commit is contained in:
str4d-transport
2014-07-07 13:35:11 +00:00
parent d3590be4d9
commit ab6e783468
7 changed files with 460 additions and 163 deletions

3
TODO
View File

@@ -1,5 +1,6 @@
Fixes:
- Figure out how to highlight email backgrounds when selected in list
- Fix clicking pictures to select emails
- Fix tick over selected emails
- Refine view email page
-- Delete/read/unread/move actions that don't break everything
- Improve network status page

View File

@@ -1,5 +1,15 @@
package i2p.bote.android;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.text.DateFormat;
@@ -11,31 +21,19 @@ import javax.mail.Part;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.email.Email;
import i2p.bote.fileencryption.PasswordException;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class EmailListAdapter extends ArrayAdapter<Email> {
private final LayoutInflater mInflater;
private SparseBooleanArray mSelectedEmails;
private EmailSelector mSelector;
private boolean mIsOutbox;
public interface EmailSelector {
public void select(int position);
public void select(View view);
}
public EmailListAdapter(Context context, EmailSelector selector, boolean isOutbox) {
super(context, android.R.layout.simple_list_item_2);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mSelectedEmails = new SparseBooleanArray();
mSelector = selector;
mIsOutbox = isOutbox;
}
@@ -51,7 +49,7 @@ public class EmailListAdapter extends ArrayAdapter<Email> {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = mInflater.inflate(R.layout.listitem_email, parent, false);
final View v = mInflater.inflate(R.layout.listitem_email, parent, false);
final Email email = getItem(position);
ImageView picture = (ImageView) v.findViewById(R.id.contact_picture);
@@ -62,13 +60,14 @@ public class EmailListAdapter extends ArrayAdapter<Email> {
picture.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
mSelector.select(getPosition(email));
mSelector.select(v);
}
});
if (mSelectedEmails.get(position)) {
((ImageView) v.findViewById(R.id.email_selected)).setVisibility(View.VISIBLE);
}
// TODO fix
//if (mSelectedEmails.get(position)) {
// ((ImageView) v.findViewById(R.id.email_selected)).setVisibility(View.VISIBLE);
//}
try {
String fromAddress = email.getOneFromAddress();
@@ -127,29 +126,4 @@ public class EmailListAdapter extends ArrayAdapter<Email> {
return v;
}
public void toggleSelection(int position) {
selectView(position, !mSelectedEmails.get(position));
}
public void removeSelection() {
mSelectedEmails = new SparseBooleanArray();
notifyDataSetChanged();
}
public void selectView(int position, boolean value) {
if (value)
mSelectedEmails.put(position, value);
else
mSelectedEmails.delete(position);
notifyDataSetChanged();
}
public int getSelectedCount() {
return mSelectedEmails.size();
}
public SparseBooleanArray getSelectedIds() {
return mSelectedEmails;
}
}

View File

@@ -1,26 +1,5 @@
package i2p.bote.android;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.List;
import javax.mail.Flags.Flag;
import javax.mail.MessagingException;
import uk.co.senab.actionbarpulltorefresh.extras.actionbarcompat.PullToRefreshLayout;
import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh;
import uk.co.senab.actionbarpulltorefresh.library.Options;
import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
import i2p.bote.I2PBote;
import i2p.bote.android.util.BetterAsyncTaskLoader;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.MoveToDialogFragment;
import i2p.bote.email.Email;
import i2p.bote.fileencryption.PasswordException;
import i2p.bote.folder.EmailFolder;
import i2p.bote.folder.FolderListener;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
@@ -28,6 +7,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.ListFragment;
@@ -43,11 +23,34 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.List;
import javax.mail.Flags.Flag;
import javax.mail.MessagingException;
import i2p.bote.I2PBote;
import i2p.bote.android.util.BetterAsyncTaskLoader;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.MoveToDialogFragment;
import i2p.bote.android.util.MultiSelectionUtil;
import i2p.bote.email.Email;
import i2p.bote.fileencryption.PasswordException;
import i2p.bote.folder.EmailFolder;
import i2p.bote.folder.FolderListener;
import uk.co.senab.actionbarpulltorefresh.extras.actionbarcompat.PullToRefreshLayout;
import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh;
import uk.co.senab.actionbarpulltorefresh.library.Options;
import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener;
public class EmailListFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<List<Email>>,
MoveToDialogFragment.MoveToDialogListener,
@@ -63,7 +66,10 @@ public class EmailListFragment extends ListFragment implements
private EmailListAdapter mAdapter;
private EmailFolder mFolder;
private ActionMode mMode;
// The Controller which provides CHOICE_MODE_MULTIPLE_MODAL-like functionality
private MultiSelectionUtil.Controller mMultiSelectController;
private ModalChoiceListener mModalChoiceListener;
private EditText mPasswordInput;
private TextView mPasswordError;
@@ -103,7 +109,7 @@ public class EmailListFragment extends ListFragment implements
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view,savedInstanceState);
super.onViewCreated(view, savedInstanceState);
String folderName = getArguments().getString(FOLDER_NAME);
mFolder = BoteHelper.getMailFolder(folderName);
@@ -138,6 +144,7 @@ public class EmailListFragment extends ListFragment implements
mNumIncompleteEmails = new TextView(getActivity());
mNumIncompleteEmails.setText(getResources().getString(R.string.incomplete_emails,
numIncompleteEmails));
mNumIncompleteEmails.setPadding(16, 5, 16, 5);
getListView().addHeaderView(mNumIncompleteEmails);
}
}
@@ -151,16 +158,15 @@ public class EmailListFragment extends ListFragment implements
setListAdapter(mAdapter);
// Set up CAB
mMode = null;
getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
onListItemSelect(position);
return true;
}
});
// Attach a MultiSelectionUtil.Controller to the ListView, giving it an instance of
// ModalChoiceListener (see below)
mModalChoiceListener = new ModalChoiceListener();
mMultiSelectController = MultiSelectionUtil
.attachMultiSelectionController(getListView(), (ActionBarActivity) getActivity(),
mModalChoiceListener);
// Allow the Controller to restore itself
mMultiSelectController.restoreInstanceState(savedInstanceState);
if (mFolder == null) {
setEmptyText(getResources().getString(
@@ -267,6 +273,16 @@ public class EmailListFragment extends ListFragment implements
getLoaderManager().initLoader(EMAIL_LIST_LOADER, null, this);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Allow the Controller to save it's instance state so that any checked items are
// stored
if (mMultiSelectController != null)
mMultiSelectController.saveInstanceState(outState);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.email_list, menu);
@@ -275,94 +291,97 @@ public class EmailListFragment extends ListFragment implements
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_new_email:
Intent nei = new Intent(getActivity(), NewEmailActivity.class);
startActivity(nei);
return true;
case R.id.action_new_email:
Intent nei = new Intent(getActivity(), NewEmailActivity.class);
startActivity(nei);
return true;
default:
return super.onOptionsItemSelected(item);
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onListItemClick(ListView parent, View view, int pos, long id) {
super.onListItemClick(parent, view, pos, id);
int position = mNumIncompleteEmails == null ? pos : pos - 1;
if (position < 0) return;
if (mMode == null) {
mCallback.onEmailSelected(
mFolder.getName(), mAdapter.getItem(position).getMessageID());
} else
onListItemSelect(position);
final Email email = (Email) getListView().getItemAtPosition(pos);
if (email != null)
mCallback.onEmailSelected(mFolder.getName(), email.getMessageID());
}
private void onListItemSelect(int position) {
mAdapter.toggleSelection(position);
boolean hasCheckedElement = mAdapter.getSelectedCount() > 0;
if (hasCheckedElement && mMode == null) {
boolean unread = mAdapter.getItem(position).isUnread();
mMode = ((ActionBarActivity) getActivity()).startSupportActionMode(new ModeCallback(unread));
} else if (!hasCheckedElement && mMode != null) {
mMode.finish();
}
if (mMode != null)
mMode.setTitle(getResources().getString(
R.string.items_selected, mAdapter.getSelectedCount()));
}
private final class ModeCallback implements ActionMode.Callback {
private class ModalChoiceListener implements MultiSelectionUtil.MultiChoiceModeListener {
private boolean areUnread;
public ModeCallback(boolean unread) {
super();
this.areUnread = unread;
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
final ListView listView = getListView();
int numChecked = 0;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
final SparseBooleanArray items = listView.getCheckedItemPositions();
for (int i = 0; i < items.size(); i++)
if (items.valueAt(i))
numChecked++;
} else
numChecked = listView.getCheckedItemCount();
mode.setTitle(getResources().getString(R.string.items_selected, numChecked));
if (checked && numChecked == 1) { // This is the first checked item
Email email = (Email) listView.getItemAtPosition(position);
areUnread = email.isUnread();
mode.invalidate();
}
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
final ListView listView = getListView();
// Respond to clicks on the actions in the CAB
switch (item.getItemId()) {
case R.id.action_delete:
SparseBooleanArray toDelete = mAdapter.getSelectedIds();
for (int i = (toDelete.size() - 1); i >= 0; i--) {
if (toDelete.valueAt(i)) {
Email email = mAdapter.getItem(toDelete.keyAt(i));
// The Loader will update mAdapter
I2PBote.getInstance().deleteEmail(mFolder, email.getMessageID());
}
}
mode.finish();
return true;
case R.id.action_mark_read:
case R.id.action_mark_unread:
SparseBooleanArray selected = mAdapter.getSelectedIds();
for (int i = (selected.size() - 1); i >= 0; i--) {
if (selected.valueAt(i)) {
Email email = mAdapter.getItem(selected.keyAt(i));
try {
case R.id.action_delete:
SparseBooleanArray toDelete = listView.getCheckedItemPositions();
if (toDelete.size() == 0)
return false;
for (int i = (toDelete.size() - 1); i >= 0; i--) {
if (toDelete.valueAt(i)) {
Email email = (Email) listView.getItemAtPosition(toDelete.keyAt(i));
// The Loader will update mAdapter
mFolder.setNew(email, !areUnread);
} catch (PasswordException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
I2PBote.getInstance().deleteEmail(mFolder, email.getMessageID());
}
}
}
areUnread = !areUnread;
mMode.invalidate();
return true;
case R.id.action_move_to:
DialogFragment f = MoveToDialogFragment.newInstance(mFolder);
f.show(getFragmentManager(), "moveTo");
return true;
default:
return false;
mode.finish();
return true;
case R.id.action_mark_read:
case R.id.action_mark_unread:
SparseBooleanArray selected = listView.getCheckedItemPositions();
for (int i = (selected.size() - 1); i >= 0; i--) {
if (selected.valueAt(i)) {
Email email = (Email) listView.getItemAtPosition(selected.keyAt(i));
try {
// The Loader will update mAdapter
mFolder.setNew(email, !areUnread);
} catch (PasswordException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
areUnread = !areUnread;
mode.invalidate();
return true;
case R.id.action_move_to:
DialogFragment f = MoveToDialogFragment.newInstance(mFolder);
f.show(getFragmentManager(), "moveTo");
return true;
default:
return false;
}
}
@@ -382,16 +401,6 @@ public class EmailListFragment extends ListFragment implements
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
// Here you can make any necessary updates to the activity when
// the CAB is removed.
mAdapter.removeSelection();
if (mode == mMode)
mMode = null;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// Here you can perform updates to the CAB due to
@@ -402,19 +411,24 @@ public class EmailListFragment extends ListFragment implements
}
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
}
}
// Called by EmailListActivity.onFolderSelected()
public void onFolderSelected(EmailFolder newFolder) {
SparseBooleanArray toMove = mAdapter.getSelectedIds();
final ListView listView = getListView();
SparseBooleanArray toMove = listView.getCheckedItemPositions();
for (int i = (toMove.size() - 1); i >= 0; i--) {
if (toMove.valueAt(i)) {
Email email = mAdapter.getItem(toMove.keyAt(i));
Email email = (Email) listView.getItemAtPosition(toMove.keyAt(i));
mFolder.move(email, newFolder);
}
}
mMode.finish();
mMultiSelectController.finish();
}
// LoaderManager.LoaderCallbacks<List<Email>>
@@ -473,7 +487,7 @@ public class EmailListFragment extends ListFragment implements
}
public void onLoadFinished(Loader<List<Email>> loader,
List<Email> data) {
List<Email> data) {
// Clear recent flags
for (Email email : data)
try {
@@ -508,8 +522,12 @@ public class EmailListFragment extends ListFragment implements
// EmailListAdapter.EmailSelector
public void select(int position) {
onListItemSelect(position);
public void select(View view) {
// TODO temporarily disabled while broken, need to fix
//final ListView listView = getListView();
//final int position = listView.getPositionForView(view);
//listView.setItemChecked(position, !listView.isItemChecked(position));
//view.performLongClick();
}
// OnRefreshListener
@@ -527,7 +545,8 @@ public class EmailListFragment extends ListFragment implements
while (I2PBote.getInstance().isCheckingForMail()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
} catch (InterruptedException e) {
}
}
return null;
}

View File

@@ -0,0 +1,293 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package i2p.bote.android.util;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.view.ActionMode;
import android.util.Pair;
import android.util.SparseBooleanArray;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.ListView;
import java.util.HashSet;
/**
* Utilities for handling multiple selection in list views. Contains functionality similar to {@link
* AbsListView#CHOICE_MODE_MULTIPLE_MODAL} which works with {@link ActionBarActivity} and
* backward-compatible action bars.
*/
public class MultiSelectionUtil {
/**
* Attach a Controller to the given <code>listView</code>, <code>activity</code>
* and <code>listener</code>.
*
* @param listView ListView which displays {@link android.widget.Checkable} items.
* @param activity Activity which contains the ListView.
* @param listener Listener that will manage the selection mode.
* @return the attached Controller instance.
*/
public static Controller attachMultiSelectionController(final ListView listView,
final ActionBarActivity activity, final MultiChoiceModeListener listener) {
return new Controller(listView, activity, listener);
}
/**
* Class which provides functionality similar to {@link AbsListView#CHOICE_MODE_MULTIPLE_MODAL}
* for the {@link ListView} provided to it. A
* {@link android.widget.AdapterView.OnItemLongClickListener} is set on the ListView so that
* when an item is long-clicked an ActionBarCompat Action Mode is started. Once started, a
* {@link android.widget.AdapterView.OnItemClickListener} is set so that an item click toggles
* that item's checked state.
*/
public static class Controller {
private final ListView mListView;
private final ActionBarActivity mActivity;
private final MultiChoiceModeListener mListener;
private final Callbacks mCallbacks;
// Current Action Mode (if there is one)
private ActionMode mActionMode;
// Keeps record of any items that should be checked on the next action mode creation
private HashSet<Pair<Integer, Long>> mItemsToCheck;
// Reference to the replace OnItemClickListener (so it can be restored later)
private AdapterView.OnItemClickListener mOldItemClickListener;
private final Runnable mSetChoiceModeNoneRunnable = new Runnable() {
@Override
public void run() {
mListView.setChoiceMode(AbsListView.CHOICE_MODE_NONE);
}
};
private Controller(ListView listView, ActionBarActivity activity,
MultiChoiceModeListener listener) {
mListView = listView;
mActivity = activity;
mListener = listener;
mCallbacks = new Callbacks();
// We set ourselves as the OnItemLongClickListener so we know when to start
// an Action Mode
listView.setOnItemLongClickListener(mCallbacks);
}
/**
* Finish the current Action Mode (if there is one).
*/
public void finish() {
if (mActionMode != null) {
mActionMode.finish();
}
}
/**
* This method should be called from your {@link ActionBarActivity} or
* {@link android.support.v4.app.Fragment Fragment} to allow the controller to restore any
* instance state.
*
* @param savedInstanceState - The state passed to your Activity or Fragment.
*/
public void restoreInstanceState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
long[] checkedIds = savedInstanceState.getLongArray(getStateKey());
if (checkedIds != null && checkedIds.length > 0) {
HashSet<Long> idsToCheckOnRestore = new HashSet<Long>();
for (long id : checkedIds) {
idsToCheckOnRestore.add(id);
}
tryRestoreInstanceState(idsToCheckOnRestore);
}
}
}
/**
* This method should be called from
* {@link ActionBarActivity#onSaveInstanceState(android.os.Bundle)} or
* {@link android.support.v4.app.Fragment#onSaveInstanceState(android.os.Bundle)
* Fragment.onSaveInstanceState(Bundle)} to allow the controller to save its instance
* state.
*
* @param outState - The state passed to your Activity or Fragment.
*/
public void saveInstanceState(Bundle outState) {
if (mActionMode != null && mListView.getAdapter().hasStableIds()) {
outState.putLongArray(getStateKey(), mListView.getCheckedItemIds());
}
}
// Internal utility methods
private String getStateKey() {
return MultiSelectionUtil.class.getSimpleName() + "_" + mListView.getId();
}
private void tryRestoreInstanceState(HashSet<Long> idsToCheckOnRestore) {
if (idsToCheckOnRestore == null || mListView.getAdapter() == null) {
return;
}
boolean idsFound = false;
Adapter adapter = mListView.getAdapter();
for (int pos = adapter.getCount() - 1; pos >= 0; pos--) {
if (idsToCheckOnRestore.contains(adapter.getItemId(pos))) {
idsFound = true;
if (mItemsToCheck == null) {
mItemsToCheck = new HashSet<Pair<Integer, Long>>();
}
mItemsToCheck.add(new Pair<Integer, Long>(pos, adapter.getItemId(pos)));
}
}
if (idsFound) {
// We found some IDs that were checked. Let's now restore the multi-selection
// state.
mActionMode = mActivity.startSupportActionMode(mCallbacks);
}
}
/**
* This class encapsulates all of the callbacks necessary for the controller class.
*/
final class Callbacks implements ActionMode.Callback, AdapterView.OnItemClickListener,
AdapterView.OnItemLongClickListener {
@Override
public final boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
if (mListener.onCreateActionMode(actionMode, menu)) {
mActionMode = actionMode;
// Keep a reference to the existing OnItemClickListener so we can restore it
mOldItemClickListener = mListView.getOnItemClickListener();
// Set-up the ListView to emulate CHOICE_MODE_MULTIPLE_MODAL
mListView.setOnItemClickListener(this);
mListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
mListView.removeCallbacks(mSetChoiceModeNoneRunnable);
// If there are some items to check, do it now
if (mItemsToCheck != null) {
for (Pair<Integer, Long> posAndId : mItemsToCheck) {
mListView.setItemChecked(posAndId.first, true);
// Notify the listener that the item has been checked
mListener.onItemCheckedStateChanged(mActionMode, posAndId.first,
posAndId.second, true);
}
}
return true;
}
return false;
}
@Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
// Proxy listener
return mListener.onPrepareActionMode(actionMode, menu);
}
@Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
// Proxy listener
return mListener.onActionItemClicked(actionMode, menuItem);
}
@Override
public void onDestroyActionMode(ActionMode actionMode) {
mListener.onDestroyActionMode(actionMode);
// Clear all the checked items
SparseBooleanArray checkedPositions = mListView.getCheckedItemPositions();
if (checkedPositions != null) {
for (int i = 0; i < checkedPositions.size(); i++) {
mListView.setItemChecked(checkedPositions.keyAt(i), false);
}
}
// Restore the original onItemClickListener
mListView.setOnItemClickListener(mOldItemClickListener);
// Clear the Action Mode
mActionMode = null;
// Reset the ListView's Choice Mode
mListView.post(mSetChoiceModeNoneRunnable);
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
// Check to see what the new checked state is, and then notify the listener
final boolean checked = mListView.isItemChecked(position);
mListener.onItemCheckedStateChanged(mActionMode, position, id, checked);
boolean hasCheckedItem = checked;
// Check to see if we have any checked items
if (!hasCheckedItem) {
SparseBooleanArray checkedItemPositions = mListView.getCheckedItemPositions();
if (checkedItemPositions != null) {
// Iterate through the SparseBooleanArray to see if there is a checked item
int i = 0;
while (!hasCheckedItem && i < checkedItemPositions.size()) {
hasCheckedItem = checkedItemPositions.valueAt(i++);
}
}
}
// If we don't have any checked items, finish the action mode
if (!hasCheckedItem) {
mActionMode.finish();
}
}
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int position,
long id) {
// If we already have an action mode started return false
// (onItemClick will be called anyway)
if (mActionMode != null) {
return false;
}
mItemsToCheck = new HashSet<Pair<Integer, Long>>();
mItemsToCheck.add(new Pair<Integer, Long>(position, id));
mActionMode = mActivity.startSupportActionMode(this);
return true;
}
}
}
/**
* @see android.widget.AbsListView.MultiChoiceModeListener
*/
public static interface MultiChoiceModeListener extends ActionMode.Callback {
/**
* @see android.widget.AbsListView.MultiChoiceModeListener#onItemCheckedStateChanged(
*android.view.ActionMode, int, long, boolean)
*/
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
boolean checked);
}
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/translucent_blue" android:state_activated="true" />
<item android:drawable="@color/translucent_blue" android:state_checked="true" />
<item android:drawable="@android:color/transparent" />
</selector>

View File

@@ -2,6 +2,7 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/listitem_checked"
android:padding="5dp" >
<ImageView

View File

@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- A translucent holo blue -->
<color name="translucent_blue">#9033B5E5</color>
<color name="uva_color">#c31756</color>
<color name="default_color">#111</color>
<color name="error_color">#f00</color>