Selección múltiple en ListView personalizado con CAB

Después de leer y tratar de evitar el error durante días, me doy por vencido y solicito ayuda.

Estoy usando ActionBarSherlock.

Lo que quiero lograr: un ListView con un diseño personalizado para cada fila, donde el usuario puede seleccionar múltiples elementos de la lista. Un elemento de lista seleccionado debe tener un color de fondo diferente. Cuando hay al menos un elemento seleccionado, se debe mostrar una barra de acción contextual (CAB). Debería verse más o menos como la selección múltiple de correos electrónicos en la aplicación GMail. La única diferencia es que en la aplicación de Gmail, la selección se realiza haciendo clic en la checkbox de una fila, mientras que no quiero tener una checkbox, pero se debe seleccionar una fila sin importar dónde haga clic el usuario. Selección múltiple en la aplicación GMail

Lo que intenté: siguiendo este tutorial , utilizando un diseño de fila comprobable con cierta lógica para cambiar el color de fondo cuando se alteró el estado de verificación, conseguí que todo funcionara, excepto que no pude registrar un oyente clic como OnItemClickListener en el ListView para mostrar el CAB . Tampoco fue útil proporcionar un escucha de clic para cada vista de fila porque esto impedía cambiar el color de fondo de los elementos seleccionados. También traté de agregar un MultiChoiceModeListener al ListView como ese

listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); listView.setMultiChoiceModeListener(new MultiChoiceModeListener() { //.. }); 

Con el mismo resultado, no cambia el color de fondo.

Lo que estoy buscando : una pista, un tutorial o un código de muestra sobre cómo hacer esto. Si necesita algunos fragmentos de código para ayudar, hágamelo saber.

Vea si el código lo ayuda (es básicamente una ListActivity con un adaptador personalizado para mantener el estado de los elementos ListActivity (+ fondo diferente)):

 public class CABSelection extends ListActivity { private ArrayList mItems = new ArrayList(); private SelectionAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); for (int i = 0; i < 24; i++) { mItems.add("Name" + i); } // R.layout.adapters_cabselection_row is a LinearLayout(with green // background(#99cc00)) that wraps an ImageView and a TextView mAdapter = new SelectionAdapter(this, R.layout.adapters_cabselection_row, R.id.the_text, mItems); setListAdapter(mAdapter); getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() { private int nr = 0; @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.cabselection_menu, menu); return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { StringBuilder sb = new StringBuilder(); Set positions = mAdapter.getCurrentCheckedPosition(); for (Integer pos : positions) { sb.append(" " + pos + ","); } switch (item.getItemId()) { case R.id.edit_entry: Toast.makeText(CABSelection.this, "Edited entries: " + sb.toString(), Toast.LENGTH_SHORT).show(); break; case R.id.delete_entry: Toast.makeText(CABSelection.this, "Deleted entries : " + sb.toString(), Toast.LENGTH_SHORT).show(); break; case R.id.finish_it: nr = 0; mAdapter.clearSelection(); Toast.makeText(CABSelection.this, "Finish the CAB!", Toast.LENGTH_SHORT).show(); mode.finish(); } return false; } @Override public void onDestroyActionMode(ActionMode mode) { nr = 0; mAdapter.clearSelection(); } @Override public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { if (checked) { nr++; mAdapter.setNewSelection(position, checked); } else { nr--; mAdapter.removeSelection(position); } mode.setTitle(nr + " rows selected!"); } }); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { l.setItemChecked(position, !mAdapter.isPositionChecked(position)); } private class SelectionAdapter extends ArrayAdapter { private HashMap mSelection = new HashMap(); public SelectionAdapter(Context context, int resource, int textViewResourceId, List objects) { super(context, resource, textViewResourceId, objects); } public void setNewSelection(int position, boolean value) { mSelection.put(position, value); notifyDataSetChanged(); } public boolean isPositionChecked(int position) { Boolean result = mSelection.get(position); return result == null ? false : result; } public Set getCurrentCheckedPosition() { return mSelection.keySet(); } public void removeSelection(int position) { mSelection.remove(position); notifyDataSetChanged(); } public void clearSelection() { mSelection = new HashMap(); notifyDataSetChanged(); } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = super.getView(position, convertView, parent);//let the adapter handle setting up the row views v.setBackgroundColor(Color.parseColor("#99cc00")); //default color if (mSelection.get(position) != null) { v.setBackgroundColor(Color.RED);// this is a selected position so make it red } return v; } } } 

R.layout.adapters_cabselection_row es un diseño personalizado para la fila (uno muy simple) con un fondo verde:

      

R.menu.cabselection_menu es un archivo de menú con 3 opciones (editar, eliminar, finalizar el CAB) que no hace nada excepto pop Toast con un mensaje con respecto a las filas seleccionadas:

       

Creo que la forma más fácil es aplicar

android:background="android:attr/activatedBackgroundIndicator"

Para cuál diseño es el que va a hacer clic.

Esto resalta el diseño cuando se selecciona usando

 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); 

trabajado para mí de todos modos

Con ActionBarSherlock, MultiChoiceModeListener utilizado en la respuesta de Luksprog aún no está disponible si desea admitir el nivel de API <11.

Una solución alternativa es usar el onItemClickListener.

Lista de configuración:

 listView = (ListView) timeline.findViewById(android.R.id.list); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); listView.setItemsCanFocus(false); listView.setAdapter(new ListAdapter(getActivity(), R.layout.cleaning_list_item, items)); 

Oyente de ListFragment o ListActivity:

 @Override public void onListItemClick(ListView l, View v, int position, long id) { SparseBooleanArray checked = listView.getCheckedItemPositions(); boolean hasCheckedElement = false; for (int i = 0; i < checked.size() && !hasCheckedElement; i++) { hasCheckedElement = checked.valueAt(i); } if (hasCheckedElement) { if (mMode == null) { mMode = ((SherlockFragmentActivity) getActivity()).startActionMode(new MyActionMode()); mMode.invalidate(); } else { mMode.invalidate(); } } else { if (mMode != null) { mMode.finish(); } } } 

Donde MyActionMode es una implementación de ActionMode.Callback:

 private final class MyActionMode implements ActionMode.Callback { /* ... */ }