Android: Problemas con ListViews y CheckBoxes

Tengo un ListView, y dentro de cada artículo de la lista tengo algunos TextViews y un CheckBox. Cuando compruebo un CheckBox y mis incendios onCheckedChangeListener, todo funciona como debería. Sin embargo, otras casillas de verificación aleatorias se comprueban una vez que se comprueba. Aquí hay un ejemplo.

Si hago clic en el primer CheckBox: 8 está marcada. 15 está marcada. 21 está marcada. 27 está marcada. 33 está marcada. 41 está marcada. Entonces, si me desplazo todo el camino, ninguno se comprueba hasta 6. El siguiente es 13.

Básicamente … ¿qué está pasando?

Parece que está reutilizando el convertView que se transmite en el método getView() que implementa.

Android intentará utilizar la misma vista para diferentes elementos de un ListView. Usted necesitará (1) desmarcar / comprobar manualmente la casilla de verificación que está dentro del elemento devuelto (siempre llame a setChecked antes de regresar en getView o (2) no use convertView, pero devuelva una nueva View from getView.

(1) se recomienda, creo.

Funciona bien para mi

 public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { final ViewHolder holder; final Season season = (Season) getGroup(groupPosition); if (convertView == null) { LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = vi.inflate(R.layout.season, parent, false); holder = new ViewHolder(); holder.title = (TextView) convertView.findViewById(R.id.season_title); holder.checkBox = (CheckBox) convertView.findViewById(R.id.season_check_box); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.title.setText(season.getTitle()); holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { season.setChecked(isChecked); adapter.notifyDataSetChanged(); } }); holder.checkBox.setChecked(season.isChecked()); // position is important! Must be before return statement! return convertView; } protected class ViewHolder { protected TextView title; protected CheckBox checkBox; } 

Yo también estaba frente a un rey similar de problema, así que después de mucho de la lectura he resuelto este problema como este:

 @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) // Check whether this row checkbox was checked, for that we previously stored the textview text in selected variable { holder.cb.setChecked(true); } return convertView; } } 

Aquí lo que estoy haciendo que en getView () estoy desmarcando todas las casillas de verificación y comprobar de nuevo manualmente los que tengo que comprobar de acuerdo con el texto que corresponde. Así que si el usuario desplaza hacia abajo después de marcar la primera casilla de verificación, toda la casilla de verificación en la vista se desmarca y si vuelve a desplazarse hasta entonces también todas las casillas de verificación se desmarcan, pero luego el que hizo clic antes se vuelve a comprobar.

El problema se puede resolver fácilmente manteniendo los estados de CheckBox . Un código de clase de muestra completo definido y explicado para el mismo es como abajo_

  public class AreaDataAdapter extends ArrayAdapter<String> { private List<String> areaList; private Activity context; ArrayList<Boolean> positionArray; public AreaDataAdapter(Context context, int textViewResourceId, List<String> offersAreaList) { super(context, textViewResourceId, offersAreaList); // TODO Auto-generated constructor stub this.context=context; this.areaList = offersAreaList; positionArray = new ArrayList<Boolean>(offersAreaList.size()); for(int i =0;i<offersAreaList.size();i++){ positionArray.add(false); } } public AreaDataAdapter(Activity context, List<String> offersAreaList) { super(ShowOffersActivity.this, R.layout.filterlist_row, offersAreaList); this.context=context; this.areaList=offersAreaList; positionArray = new ArrayList<Boolean>(offersAreaList.size()); for(int i =0;i<offersAreaList.size();i++){ positionArray.add(false); } } public View getView(final int position, View convertView,ViewGroup parent) { View row=convertView; FilterViewHolder holder; if (row==null) { LayoutInflater inflater=getLayoutInflater(); row=inflater.inflate(R.layout.filterlist_row, parent, false); holder = new FilterViewHolder(); holder.filterCheckBox = (CheckBox)row.findViewById(R.id.filter_checkbox); holder.filterCheckBox.setTypeface(fontFace); row.setTag(holder); } else { //holder = (View) row; holder = (FilterViewHolder) row.getTag(); /* When a listview recycles views , it recycles its present state as well as listeners attached to it. * if the checkbox was checked and has a onCheckedChangeListener set, both will remain a part of * recycled view based on position. So it is our responsibility to reset all states and remove * previous listeners. * The listener was removed as below:- */ holder.filterCheckBox.setOnCheckedChangeListener(null); } holder.filterCheckBox.setText(areaList.get(position)); holder.filterCheckBox.setFocusable(false); holder.filterCheckBox.setChecked(positionArray.get(position)); holder.filterCheckBox.setText(areaList.get(position)); holder.filterCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // TODO Auto-generated method stub if (isChecked) { //change state of concern item in 'positionArray' array to 'True' positionArray.set(position, true); //add the checked item in to area list which used to filter offers. filterOffersByThisAreaList.add(areaList.get(position)); } else { //change state of concern item in 'positionArray' array to 'True' positionArray.set(position, true); //remove the unchecked item in to area list which used to filter offers. filterOffersByThisAreaList.remove(areaList.get(position)); } } }); return row; } } 

Fila personalizada (filterlist_row) para el ListView contiene estos CheckBox s es como below_

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <CheckBox android:id="@+id/filter_checkbox" style="@style/CodeFont" android:button="@drawable/bg_custom_checkbox" android:text="Indian" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#c9c9c3" /> </LinearLayout> 

Personalizar CheckBox backgroung es como below_

 <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/checkbox_s" android:state_checked="true"></item> <item android:drawable="@drawable/checkbox_ns" android:state_checked="false"></item> </selector> 

Tuve el mismo tipo de problema con mi vista de interruptor. Después de un montón de búsqueda y lectura, me imagino que este problema es el resultado de una especie de optimización de las vistas promovidas por Android. Intenta reutilizar los objetos de vistas. Por lo tanto, básicamente, al hacer clic en un conmutador o una casilla de verificación que activar la devolución de llamada de cambio en otra vista también. La optimización realmente funciona, pero si no prestas atención, o simplemente no conoces el comportamiento (como yo), ocurren algunos resultados bastante extraños.

De todos modos, encuentro esta solución sencilla:

La mejor práctica es reutilizar el convertView pasado en el método getView (). De esta manera se optimiza la cantidad de RAM utilizada por el listview. Así que guardo mis vistas en un ViewHolder y programé el evento onCheckedChanged. El truco es, antes de establecer si el interruptor está marcado o no (en su caso, marque la casilla de verificación) y definir el escuchador CheckedChange, simplemente restablecer la devolución de llamada con null, esto asegura que el evento de otro conmutador no se activa.

Aquí está el fragmento:

 ... viewHolder.switchView.setOnCheckedChangeListener(null); viewHolder.switchView.setChecked(myObject.getStatus() > 0); ... viewHolder.switchView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { ... } }); 

MyObject es un objeto final que tiene los datos del cursor almacenados.

Y sin importar el interruptor será comprobado o no, usted tiene que llamar el método setChecked.

Espero que ayude a alguien!

  • ListView / ExpandableListView setEmptyView () no tiene efecto
  • Devolución de valores de múltiples selecciones ListView
  • Un ListView puede contener fragmentos
  • Android ListView áreas parciales presionables
  • ListView dinámico en Android Studio: Selección de elemento aleatorio y vista de lista de actualizaciones
  • Motorola Droid X, Android 2.3.3 divisor de filas de vista de lista
  • AbsListView unregisterIRListener () se llama
  • ¿Qué es android.R.layout.simple_list_item_1?
  • CheckedTextView no marca de selección de dibujo cuando se selecciona mediante programación
  • ¿Cómo actualizar dinámicamente un ListView con un adaptador personalizado?
  • Actualizaciones de datos de Android ListView ArrayAdapter Mejores prácticas
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.