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.
- Modo de elección en un RecyclerView?
- Sincronizar todo el ListView en un ViewPager
- ¿Cómo activar el desplazamiento rápido (pulgar) en el menú desplegable de Android Spinner?
- SwapCursor undefined error
- Android: Cómo rellenar una vista de lista con elementos de matriz
Básicamente … ¿qué está pasando?
- ¿No puedo hacer clic en el ListView en android?
- Stick Android ListView "categorías" para arriba
- Android: el evento onItemClick () de ListView no se llama
- Cómo añadir una sugerencia de texto a Android SwipeRefreshLayout
- ArrayList Adapter (para ListView) sólo muestra el primer elemento agregado
- Adaptador de ListView personalizado lanza UnsupportedOperationException
- El objetivo no debe ser nulo utilizando la Biblioteca Picasso
- ListView de Android: Recurso no encontrado Excepción
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!