2012-09-28 43 views
8

Tengo un ListView que utiliza un adaptador personalizado como se muestra:Listview con adaptador personalizado que contiene CheckBoxes

private class CBAdapter extends BaseAdapter implements OnCheckedChangeListener{ 

    Context context; 
    public String[] englishNames; 
    LayoutInflater inflater; 
    CheckBox[] checkBoxArray; 
    LinearLayout[] viewArray; 
    private boolean[] checked; 

    public CBAdapter(Context con, String[] engNames){ 
     context=con; 
     englishNames=engNames; 
     inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     checked= new boolean[englishNames.length]; 
     for(int i=0; i<checked.length; i++){ 
      checked[i]=false; 
      //Toast.makeText(con, checked.toString(),Toast.LENGTH_SHORT).show(); 
     } 
     checkBoxArray = new CheckBox[checked.length]; 
     viewArray = new LinearLayout[checked.length]; 
    } 

    public int getCount() { 
     return englishNames.length; 
    } 

    public Object getItem(int position) { 
     // TODO Auto-generated method stub 
     return null; 
    } 

    public long getItemId(int position) { 
     // TODO Auto-generated method stub 
     return 0; 
    } 

    public View getView(int position, View convertView, ViewGroup parent) { 

     if(viewArray[position] == null){ 

      viewArray[position]=(LinearLayout)inflater.inflate(R.layout.record_view_start,null); 

      TextView tv=(TextView)viewArray[position].findViewById(R.id.engName); 
      tv.setText(englishNames[position]); 

      checkBoxArray[position]=(CheckBox)viewArray[position].findViewById(R.id.checkBox1); 
     } 

     checkBoxArray[position].setChecked(checked[position]); 
     checkBoxArray[position].setOnCheckedChangeListener(this); 
     return viewArray[position]; 
    } 


    public void checkAll(boolean areChecked){ 
     for(int i=0; i<checked.length; i++){ 
      checked[i]=areChecked; 
      if(checkBoxArray[i] != null) 
       checkBoxArray[i].setChecked(areChecked); 
     } 
     notifyDataSetChanged(); 
    } 

    public void onCheckedChanged(CompoundButton cb, boolean isChecked) { 
     for(int i=0; i<checked.length; i++){ 
      if(cb == checkBoxArray[i]) 
       checked[i]=isChecked; 
     } 




    } 
    public boolean itemIsChecked(int i){ 
     return checked[i]; 
    } 

} 

Los diseños son bastante simples, así que no publicarlos menos que alguien piensa que son relevantes.

El problema es que algunas de las CheckBoxes no responden. Parece ser los que están visibles cuando el diseño se muestra por primera vez. Cualquiera que tenga que desplazarse hacia abajo para trabajar como se espera.

Cualquier puntero apreciado.

Respuesta

15

Su código de las obras respuesta, pero es ineficiente (en realidad se puede ver esto, simplemente desplazar la ListView y comprobar el Logcat para ver el recolector de basura haciendo su trabajo). Un getView método mejorado que permita el reciclaje de vistas es la siguiente:

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 
    LinearLayout view = (LinearLayout) convertView; 
    if (view == null) { 
      view = (LinearLayout) inflater.inflate(R.layout.record_view_start, parent, false); 
    } 
    TextView tv = (TextView) view.findViewById(R.id.engName); 
    tv.setText(getItem(position)); 
    CheckBox cBox = (CheckBox) view.findViewById(R.id.checkBox1); 
    cBox.setTag(Integer.valueOf(position)); // set the tag so we can identify the correct row in the listener 
    cBox.setChecked(mChecked[position]); // set the status as we stored it   
    cBox.setOnCheckedChangeListener(mListener); // set the listener  
    return view; 
} 

OnCheckedChangeListener mListener = new OnCheckedChangeListener() { 

    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
     mChecked[(Integer)buttonView.getTag()] = isChecked; // get the tag so we know the row and store the status 
    } 
}; 

En cuanto a su código de su pregunta, al principio pensé que era un error debido a la forma en que la configuración de las filas, pero no veo por qué el adaptador tendrá ese comportamiento cuando separe la vista de fila de la lista. Además, incluso probé el código y funciona bastante bien con respecto a CheckBoxes (pero con un manejo de memoria muy pobre). ¿Tal vez estás haciendo otra cosa que hace que el adaptador no funcione?

+0

¿Cómo se hace esto para una EcxpandableListView con casillas de verificación secundarias? – RicNjesh

+0

@RicNjesh Deberá usar el método 'getChildView()' que se llama siempre que se requiera una fila secundaria. – Luksprog

+0

¿Qué se controla en este caso y cómo declararlo? –

1

Déjame decir primero que has descartado uno de los principales beneficios de usar un adaptador: vistas reutilizables. Llevar a cabo una referencia difícil de cada View creado tiene un alto riesgo de golpear el techo de la memoria. Debería reutilizar convertView cuando no es nulo y crear su vista cuando convertView es nulo. Hay muchos tutoriales que le muestran cómo hacerlo.

Vistas utilizados en un adaptador normalmente tienen un OnClickListener unidos a ellos por el padre View de manera que se puede establecer un OnItemClickListener en el ListView. Esto reemplazará a todos los oyentes táctiles en las vistas individuales. Intente configurar android:clickable="true" en el CheckBox en XML.

+0

Gracias por la información sobre convertView. Me había estado preguntando para qué era este parámetro. Creo que puedo tener una solución al problema original, pero todavía estoy probando. Volveré a publicar pronto – s1ni5t3r

0

Puede que esta no sea la solución más elegante o eficiente pero funciona para mi situación. Por alguna razón, el intento de reutilizar las vistas desde una matriz de vistas o mediante el uso de convertView hace que todo varíe y los CheckBoxes no responden.

Lo único que funcionó fue crear una nueva Vista cada vez que se llama a getView().

public View getView(final int position, View convertView, ViewGroup parent) { 
     LinearLayout view; 
     view=(LinearLayout)inflater.inflate(R.layout.record_view_start,null); 


     TextView tv=(TextView)view.findViewById(R.id.engName); 
     tv.setText(englishNames[position]); 

     CheckBox cBox=(CheckBox)view.findViewById(R.id.checkBox1); 
     cBox.setChecked(checked[position]); 
     cBox.setOnCheckedChangeListener(new OnCheckedChangeListener(){ 

      public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
       checked[position]=isChecked; 
      } 
     }); 
     return view; 
    } 

Encontrar esta solución también se vio obstaculizado por el hecho de que yo estaba llamando a un onCheckedChangedListener definido por separado, que luego identificados, que CheckBox por id, en lugar de tener una nueva escucha para cada casilla de verificación.

Hasta ahora no he marcado esto como la respuesta correcta, ya que espero que otros puedan tener alguna opinión con respecto a la reconstrucción cada vez más derrochador de la vista.

+1

Hay una gran cantidad de preguntas sobre stackoverflow con respecto a un 'ListView' con un adaptador personalizado que utiliza una fila con un' CheckBox', debería mirar más a esas soluciones. Su código funciona pero es muy ineficiente, cada vez que se despliega 'ListView' creará nuevas vistas. – Luksprog

+0

Estoy completamente de acuerdo con Luksprog, pero después de muchos intentos al respecto, al usar soluciones que funcionan para otros, no puedo encontrar una solución que funcione de otra manera. Afortunadamente, la recolección de basura se mantendrá al día con las vistas desperdiciadas y no causará un problema de memoria, y los teléfonos más antiguos no se ralentizarán demasiado. En mi aplicación, en la mayoría de las situaciones, la vista de lista no tendrá demasiadas entradas, por lo que el desplazamiento debe mantenerse al mínimo. – s1ni5t3r

+1

La razón por la cual su código funciona ahora es porque usted infla la fila cada vez y evita que el 'OnCheckedChangeListener' arruine el estado' CheckBox'. Nunca debe ignorar el reciclaje de vistas en el método 'getView'. Aquí está su método mejorado 'getView' que debería hacer lo que quiera, pero también reciclará las vistas. https://gist.github.com/3917222 – Luksprog

Cuestiones relacionadas