2010-09-07 28 views
7

Tengo un ListView, y dentro de cada elemento de la lista tengo algunos TextViews y un CheckBox. Cuando miro un CheckBox y mis incendios onCheckedChangeListener, todo funciona como debería. Sin embargo, se verifican casillas de verificación al azar una vez que se marca una. Aquí hay un ejemplo.Android: Problema con ListViews y CheckBoxes

Si hago clic en la primera casilla de verificación: 8 está marcada. 15 está marcado. 21 está marcado. 27 está marcado. 33 está marcado. 41 está marcado. Luego, si me desplazo hacia arriba, ninguno se comprueba hasta 6. El siguiente es 13.

Básicamente ... ¿qué está pasando?

Respuesta

10

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

Android intentará usar la misma vista para diferentes elementos en un ListView. Deberá (1) desmarcar/marcar manualmente la casilla de verificación que está dentro del artículo devuelto (siempre llame al setChecked antes de volver a getView o (2) no utilice convertView, pero devuelva una nueva Vista de getView.

(1) se recomienda, creo

+0

OK, así que fui con la opción uno. Justo antes de devolver la Vista en getView, hago "checkBox.setChecked (false);". Ahora, sin embargo, cuando marqué una casilla de verificación, me desplazo hacia abajo y hacia arriba, ya no está marcada. Sé que el problema es con mi implementación, y no con su concepto. ¿Qué estoy haciendo mal? –

+0

Cuando el usuario marca la casilla de verificación, debe almacenar que la casilla de verificación del elemento ha sido marcada, en algún lugar de su código. Por ejemplo, si tiene 40 elementos en su ListView, puede tener una matriz de booleanos para almacenar si se marca una casilla de verificación de i-th. Luego, en 'getView', haga checkBox.setChecked (booleanArray [position]). – yuku

+0

Tal vez sea porque es muy tarde en la noche, pero no puedo entender cómo grabar la posición. En el método getView, puedo tener la posición, pero en OnClick, donde necesito almacenar la posición, ya no tengo acceso a ella. –

5

funciona bien para mí

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; 
    } 
+0

No sé qué temporada es, pero para mí esto parece estar funcionando. Para otros que tienen un problema (personalmente no tuve problemas con las casillas de verificación, pero el efecto de tachado apareció aleatoriamente en los ítems). Entonces: titular final de ViewHolder; Y el setOnCheckedChangeListener fue las dos cosas que hicieron funcionar las cosas. Espero que esto ayude a otros! ¡Y gracias Georgy! – erdomester

+0

su increíble yarr..i hice un montón de R & D. Esta es la mejor solución –

+0

@Georgy Gobozov por favor vea también este problema https://stackoverflow.com/questions/44561788/checkbox-checked-unchecked-state-changes -on-scroll-listview-baseadapter # –

0

también estaba frente a un rey similar de problema, así que después de mucha 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 en getView() Estoy desmarcando todas las casillas de verificación y comprobando de nuevo manualmente aquellas que necesito verificar según la vista de texto que corresponda. Entonces, si el usuario se desplaza hacia abajo después de marcar la primera casilla de verificación, 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.

0

El problema se puede resolver fácilmente manteniendo los estados CheckBox. Un auto definido y es tan below_

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; 
    } 

} 

personalizada Fila (filterlist_row) para la ListView que contienen estas CheckBox s código completo de clase de ejemplo explicó para el mismo es tan 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 tan 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> 
0

Tuve el mismo tipo de problema con mi vista de cambio. Después de mucha búsqueda y lectura, descubro que este problema es el resultado de una especie de optimización de las vistas promovidas por Android. Intenta reutilizar los objetos de vistas. Entonces, básicamente, cuando haces clic en un interruptor o en una casilla de verificación, disparas 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), suceden resultados bastante extraños.

De todos modos, creo que esta solución sencilla:

Lo más recomendable es volver a utilizar el convertView pasó en el método getView(). De esta forma, optimiza la cantidad de Ram utilizada por la lista. Así que guardo mis vistas en un ViewHolder y programé el evento onCheckedChanged. El truco es que, antes de establecerse si el interruptor está marcado o no (en su caso, marque la casilla de verificación) y defina el oyente CheckedChange, simplemente restableceré la devolución de llamada con nulo, esto asegura que el evento de otro interruptor no se active.

Aquí es 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 almacenados de cursor.

Independientemente de que el interruptor se verifique o no, debe llamar al método setChecked.

Espero que ayude a alguien!

0
@Override 
public int getViewTypeCount() { 
    return getCount(); 
} 

@Override 
public int getItemViewType(int position) { 
    return position; 
} 

@Override 
public int getCount() { 
    return names.length; 
} 

@Override 
public long getItemId(int position) { 
    return 0; 
} 

agregue este método en el adaptador personalizado. Está funcionando para mí

Cuestiones relacionadas