Obteniendo un problema al verificar la casilla generada dinámicamente a través de la vista de lista

Sé que otros miembros ya han formulado esta pregunta y algunos miembros también han dado una solución, pero lo cierto es que no encontré ninguna solución adecuada para mi aplicación. Estoy creando una aplicación en la que tengo una pantalla que mostrará la vista de lista dinámica con elementos de lista, una checkbox y tres textviews (una para el nombre del candidato y otras dos para clockIn y clockOut time que se mostrarán después de elegir la fecha y la hora Ahora mi problema es que cuando marque la primera checkbox (tengo 15 nombres de candidatos con casillas de verificación) automáticamente la 10ma checkbox se verificará a sí misma y esto también sucede con 2nd y 11th, 3rd & 12th y así sucesivamente (viceversa también cierto) .aquí estoy proporcionando mi clase de adaptador y lista elemento xml.

import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.content.Context; import android.util.SparseBooleanArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.TextView; import android.widget.Toast; import com.android.feedback.ListViewCheckBox; public class DemoAdapter extends ArrayAdapter{ private final List list; private final Activity context; LayoutInflater inflater; TextView CItv,COtv; static ViewHolder holder; View view; public DemoAdapter(Activity context, List list) { super(context, R.layout.test_listitems,list); // TODO Auto-generated constructor stub this.context = context; this.list = list; } static class ViewHolder { protected TextView text,CItv,COtv; protected CheckBox checkbox; } @Override public View getView(final int position, View convertView, ViewGroup parent) { view = null; // final ArrayList checkedItems = new ArrayList(); if (convertView == null) { inflater = context.getLayoutInflater(); view = inflater.inflate(R.layout.test_listitems, null); final ViewHolder viewHolder = new ViewHolder(); viewHolder.CItv = (TextView)view.findViewById(R.id.CITextView); viewHolder.COtv = (TextView)view.findViewById(R.id.COTextView); viewHolder.text = (TextView) view.findViewById(R.id.empTextView); viewHolder.checkbox = (CheckBox) view.findViewById(R.id.empCheckBox); viewHolder.checkbox .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if(isChecked){ Object o = getItemId(position+1); String keyword = o.toString(); Toast.makeText(getContext(), "You selected: " + keyword, 2000).show(); Toast.makeText(getContext(),ListViewCheckBox.DT_selected, 2000).show(); // holder.CItv.setText(ListViewCheckBox.DT_selected); // holder.COtv.setText(ListViewCheckBox.outDT_selected); } else{ Object o = getItemId(position+1); String keyword = o.toString(); //Toast.makeText(getContext(), "You unselected: " + keyword, 2000).show(); holder.CItv.refreshDrawableState(); holder.COtv.refreshDrawableState(); } } }); view.setTag(viewHolder); viewHolder.checkbox.setTag(list.get(position)); viewHolder.checkbox.setId(position); } else { view = convertView; ((ViewHolder) view.getTag()).checkbox.setTag(list.get(position)); } holder = (ViewHolder) view.getTag(); holder.text.setText(list.get(position)); return view; } } 

y XML.

            

Ayúdame a solucionar el problema. (ListViewCheckBox es una clase que genera una lista y almacena el valor de la fecha y la hora en las variables DT_selected y outDT_selected).

Edité mi respuesta para que la información común se encuentre en la parte superior. Encontrarás la respuesta real a esta pregunta en la parte inferior …


Aquí está la idea real y el proceso de reciclaje para que pueda averiguar qué está mal con su implementación y la idea de getView (y tal vez otros también cuando encuentren esta pregunta y respuesta). Consulte más abajo un ejemplo de código, simplemente ignore la parte de tipo ya que esta es información adicional.

  • Fase 1: creación de elementos para el reciclaje ( convertView es null ):
    Esto significa que crea el diseño y el estado común que comparten todos los elementos. Si tiene oyentes, los ha agregado aquí y los diseña de esa manera para que puedan reactjsr sobre los cambios de posición (cuando se vuelva a utilizar) más adelante. Por ejemplo, estableciendo la posición como etiqueta en la vista correspondiente para que el oyente pueda captar esta información y saber en qué elemento está funcionando actualmente. No puede usar las vistas para almacenar datos. Entonces, cuando el oyente cambia un estado en un elemento de la lista, debe conservar estos datos (en una matriz de datos, en una base de datos SQLite, etc.) y usarlos en la fase 2 .

  • Fase 2: estado del elemento de configuración para la posición dada:
    Establece el estado visual para el elemento. Todo lo que podría cambiar de forma individual para un elemento (texto, estado de la checkbox, colores, etc.) debe configurarse aquí. No solo lo que ha cambiado para el artículo actual sino que podría haber sido cambiado por otro artículo. De esta forma, se asegura de que la vista no se use en un estado no válido porque se está reutilizando desde otro elemento de la lista anteriormente.


La respuesta acordada fue eliminada / editada, pero sugirió implementar getItemViewType y getViewTypeCount para que cada elemento de la lista tuviera su propio tipo de vista. La respuesta editada muestra ahora cómo resolver el problema tal como se describe aquí.

Reimplementar getItemViewType y getViewTypeCount funciona pero obviamente malinterpretar su uso (compare mi ejemplo más abajo y / o esta respuesta ).

Estos dos métodos están ahí para usar dos (o más) elementos de lista que difieren completamente entre sí (por ejemplo, un elemento de lista común y un separador que solo contiene un título) y no para evitar el reciclaje de una vista que podría reutilizarse.

Si los usa de todos modos para resolver su problema, probablemente no entendió el proceso que expliqué antes. Entonces, por ejemplo, tienes 1000 elementos y haces el tipo de vista pirateado, entonces estás creando 1000 vistas (jerarquías), en cambio, probablemente 10, que podrían reutilizarse fácilmente . Eso no debería importar mucho si solo tienes 20 elementos más o menos, pero si usas esa técnica para listas grandes, ¡estás desperdiciando la (preciosa) memoria!

Aquí hay un ejemplo:

 void getItemViewType(int position) { return isItemAtPositionSeperator(position) ? 1 : /* normal item */ 0; } void int getViewTypeCount() { return 2; // normal item and separator } void View getView(int position, View convertView, ViewGroup parent) { int type = getItemViewType(position); // phase 1: see my explanation if (convertView == null) { if (type == 0) { // setup your common item view - inflate it and set to convertView } else { // setup separator view - inflate it and set to convertView } } // phase 2: see my explanation if (type == 0) { // set the state of the common item view based on the position // rely on the fact that convertView contains the view hierarchy // you created in convertView == null && type == 0 } else { // set state of the separator based on the position // rely on the fact that convertView contains the view hierarchy // you created in convertView == null && type != 0 (else part) } return convertView; } 

Respuesta real a la pregunta …

Sé cuál es el problema pero no puedo pensar en una solución elegante en este momento …

Su problema es que establece el escucha de clic una vez con viewHolder.checkbox.setOnCheckedChangeListener cuando se crea la vista. Por lo tanto, se recicla / reutiliza para los artículos cuando se desplaza y el comportamiento del clic se aplica al elemento de la lista incorrecta.

Intente no codificar la posición utilizando la final position externa. Intenta configurar viewHolder.checkbox.setTag(position) antes de return y luego usa (Integer) buttonView.getTag() lugar position+1 . Por lo tanto, su vista reciclada mantendrá la posición real.

Cuando hace clic en una checkbox, debe persistir el estado en otro lugar. No confíe en el estado de UI para eso (porque será reciclado). Así que llame a viewHolder.checkbox.setChecked(persistedState) antes de return .

Espero que esto tenga sentido y entiendas la idea … 😉

Prueba esto,

Cree una clase POJO que mantendrá el estado de los elementos seleccionados de Checkbox como este,

 public class Model { private String name; private boolean selected; public Model(String name) { this.name = name; selected = false; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isSelected() { return selected; } public void setSelected(boolean selected) { this.selected = selected; } } 

Y esto es lo que debe aplicar al método getView() en el Adaptador.

 public View getView(int position, View convertView, ViewGroup parent) { checkBoxCounter = 0; checkBoxInitialized = 0; if (convertView == null) { final ViewHolder viewHolder = new ViewHolder(); LayoutInflater inflator = context.getLayoutInflater(); convertView = inflator.inflate(R.layout.main, null); viewHolder.text = (TextView) convertView.findViewById(R.id.label); viewHolder.checkbox = (CheckBox) convertView.findViewById(R.id.check); viewHolder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { Model element = (Model) viewHolder.checkbox.getTag(); element.setSelected(buttonView.isChecked()); if(checkBoxCounter <= checkBoxInitialized){ // increment counter, when we scroll the List it execute onCheckedChanged everytime so by using this stuff we can maintain the state checkBoxCounter++; } else{ Model element = (Model) viewHolder.checkbox.getTag(); element.setSelected(buttonView.isChecked()); if(element.isSelected()) Toast.makeText(getContext(), "You selected "+ element.getName(), Toast.LENGTH_LONG).show(); else Toast.makeText(getContext(), "Not selected "+ element.getName(), Toast.LENGTH_LONG).show(); } } }); convertView.setTag(viewHolder); viewHolder.checkbox.setTag(list.get(position)); } else{ ((ViewHolder) convertView.getTag()).checkbox.setTag(list.get(position)); } ViewHolder viewHolder = (ViewHolder) convertView.getTag(); viewHolder.text.setText(list.get(position).getName()); viewHolder.checkbox.setChecked(list.get(position).isSelected()); return convertView; } 

Para obtener más información sobre cómo funciona esto, puede echar un vistazo al ejemplo completo. Y también puedes echar un vistazo a cómo funciona ListView

ACTUALIZACIÓN: Recientemente, agregué una solución para este tipo de problema por blog. ListView con CheckBox Desplazamiento Problema

visite este enlace a continuación y desplácese a vrs único Multiselection. Aquí encontrará un muy buen ejemplo para usar casillas de verificación en listview

(desplácese hacia abajo a Single vrs. Multiselection)

http://www.vogella.de/articles/AndroidListView/article.html

y también

Casilla de verificación en vista de lista con encuadernación Custom SimpleCurser