Android: CursorAdapter, ListView y CheckBox

Tengo ListView con mi propio diseño y CustomCursorAdapter. Cada fila tiene su propia checkbox. Entonces … está absolutamente claro que durante el sroll las casillas de verificación pierden sus estados. Lo único que encontré es Android save Checkbox State en ListView con Cursor Adapter pero no hay respuesta allí. Y una pregunta más. Tuve el mismo problema con mi CustorArrayAdapter. Resolví ese problema usando SparseBooleanArray para mantener los estados de las casillas de verificación. Funciona bien, pero cada desplazamiento hace clic enCheckedChanged. ¿Eso es normal? La oferta es mi vista de lista describe elementos de alarma y llamadas periódicas (de onCheckedChanged) iniciar / detener las alarmas. Una gran cantidad de acciones de uncancesario.

Hay algunas inquietudes con el ListView cuando tiene elementos que pueden marcarse. Sugeriría el siguiente enlace:

http://tokudu.com/2010/android-checkable-linear-layout/

Creo que está cerca de lo que quieres.

Tuve un problema similar con mi ListView con CheckBox y lo que hice para deshacerme del problema:

  • Cree una ArrayList of Boolean Object para almacenar el estado de cada CheckBox
  • Inicializa los elementos de ArrayList en el valor predeterminado false , significa que aún no se ha marcado CheckBox.
  • Cuando haces clic en CheckBox. Establezca una comprobación en estado Controlado / No verificado y almacene ese valor en ArrayList.
  • Ahora establece esa posición en CheckBox usando el método setChecked ().

Vea este fragmento de código:

 public class MyDataAdapter extends SimpleCursorAdapter { private Cursor c; private Context context; private ArrayList list = new ArrayList(); private ArrayList itemChecked = new ArrayList(); // itemChecked will store the position of the checked items. public MyDataAdapter(Context context, int layout, Cursor c, String[] from, int[] to) { super(context, layout, c, from, to); this.c = c; this.context = context; for (int i = 0; i < this.getCount(); i++) { itemChecked.add(i, false); // initializes all items value with false } } public View getView(final int pos, View inView, ViewGroup parent) { if (inView == null) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); inView = inflater.inflate(R.layout.your_layout_file, null); } final CheckBox cBox = (CheckBox) inView.findViewById(R.id.bcheck); // your // CheckBox cBox.setOnClickListener(new OnClickListener() { public void onClick(View v) { CheckBox cb = (CheckBox) v.findViewById(R.id.your_checkbox_id); if (cb.isChecked()) { itemChecked.set(pos, true); // do some operations here } else if (!cb.isChecked()) { itemChecked.set(pos, false); // do some operations here } } }); cBox.setChecked(itemChecked.get(pos)); // this will Check or Uncheck the // CheckBox in ListView // according to their original // position and CheckBox never // loss his State when you // Scroll the List Items. return inView; }} 

También me enfrentaba a un problema similar, así que después de leerlo, resolví este problema de esta manera:

 @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = mInflater.inflate(R.layout.listview, null); holder = new ViewHolder(); holder.nameView = (TextView)convertView.findViewById(R.id.textView1); holder.numberView = (TextView)convertView.findViewById(R.id.textView2); holder.cb = (CheckBox)convertView.findViewById(R.id.checkBox1); convertView.setTag(holder); } else { holder = (ViewHolder)convertView.getTag(); } holder.nameView.setText(mData.get(position).toString()); holder.numberView.setText(mNumber.get(position).toString()); holder.cb.setChecked(false); holder.cb.setTag(position); if(selected.indexOf(mNumber.get(position).toString()) >= 0) { holder.cb.setChecked(true); } return convertView; } } 

Aquí lo que estoy haciendo en getView () estoy desmarcando todas las casillas de verificación y comprobando de nuevo manualmente aquellas que debo verificar según la vista de texto que corresponda. Entonces, si el usuario se desplaza hacia abajo después de marcar la primera checkbox, todas las casillas de la vista se desmarcarán y si vuelve a desplazarse también se desactivarán todas las casillas de verificación, pero luego se volverá a verificar nuevamente el que hizo clic anteriormente.

Decidí probar con el método setViewBinder. El estado de las heckboxes se almacena en SparseBooleanArray. El estado de la checkbox cambia al hacer clic en la checkbox y en toda la celda.

Mi fila: | TextView | CheckBox |

 public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // .. private SparseBooleanArray checkedItems = new SparseBooleanArray(); //for storing item state startManagingCursor(cursor); String[] from = new String[] { dbAdapter.COLUMN_NAME, dbAdapter.COLUMN_CHECK }; int[] to = new int[] { R.id.item_name, R.id.item_check }; recordsAdapter = new SimpleCursorAdapter(this, R.layout.items, cursor, from, to); recordsAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() { public boolean setViewValue(View view, Cursor cursor, int columnIndex) { if (columnIndex == 2) { //2 - R.id.item_check final CheckBox cb = (CheckBox) view; final int rowID = cursor.getInt(0); //cursor.getInt(0) - _id from table if (checkedItems.indexOfKey(rowID) >= 0) { //checkedItems contains rowID? cb.setChecked(checkedItems.get(rowID)); } else if (cursor.getInt(2) > 0) { //cursor.getInt(2): 0 - false, 1 - true checkedItems.append(rowID, true); cb.setChecked(true); } else { cb.setChecked(false); } cb.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (checkedItems.indexOfKey(rowID) >= 0) { checkedItems.put(rowID, !(checkedItems.get(rowID))); } else { checkedItems.append(rowID, true); } cb.setChecked(checkedItems.get(rowID)); dbAdapter.updateItem(rowID, checkedItems.get(rowID)?1:0); } }); return true; } return false; } }); itemsList.setAdapter(recordsAdapter); itemsList.setOnItemClickListener(this); // .. } //if click on TextView of row - CheckBox of row is set/unset @Override public void onItemClick(AdapterView parent, View view, int pos, long id) { TextView tv = (TextView) view.findViewById(R.id.item_name); ViewGroup row = (ViewGroup) tv.getParent(); CheckBox cb = (CheckBox) row.getChildAt(1); cb.performClick(); } 

No puedo entender por qué no es posible (como en HTML) definir un ViewElement como una matriz en XML. Es decir:

  

y almacenar un valor int it [true | false]. Esto haría la vida mucho más fácil, en lugar de usar fonts complicadas. Además, los datos (es decir, al cambiar de paisaje a retrato) deben mantenerse en Android y no perderse.

Quizás en API 117, versión toothscratch …

(Sry, aún no puedo comentar) – con respecto a la Respuesta de Vikas Patidar, que funciona para mí – tenga cuidado al usar carga asíncrona (como usar un cargador) porque getCount en el Constructor del adaptador será cero.

Terminas con errores de indexación (porque ArrayList nunca se llena). Utilice SparseBooleanArray en lugar de ArrayList y también funcionará con carga asíncrona.

Podría llegar tarde aquí, para sumr la respuesta de Vikas Patidar, aquí hay una implementación completa que utiliza el método bindView para aquellos de ustedes que están demandando un CursorAdapter:

 @Override public void bindView(View view, Context context, Cursor cursor) { final ViewHolder holder = (ViewHolder) view.getTag(); final int position = cursor.getPosition(); String name = (cursor.getString(cursor.getColumnIndexOrThrow("NotificationDateFor"))); String image = cursor.getString(cursor.getColumnIndexOrThrow("imageUri")); System.out.println("cursor.getPosition(test): "+cursor.getPosition()); holder.nametext.setText(name); // setImage(image, holder.iv); holder.chk.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { if (holder.chk.isChecked()) { itemChecked.set(position, true); System.out.println("cursor.getPosition(true): "+position); } else if (!holder.chk.isChecked()) { itemChecked.set(position, false); System.out.println("cursor.getPosition(false): "+position); // AddDelete } } }); holder.chk.setChecked(itemChecked.get(cursor.getPosition())); } 

Nota: final int position = cursor.getPosition(); ¡es significante!

Puede ahorrarse muchos problemas utilizando el modo de selección múltiple integrado de Listview.

listView.setChoiceMode (ListView.CHOICE_MODE_MULTIPLE);