2011-08-15 13 views
7

Estoy haciendo mi primera aplicación de Android y tengo un problema por el que no puedo encontrar la respuesta en ningún lugar de Google.Android Cómo saber qué casilla de verificación está seleccionada

Quiero una lista de elementos con casillas de verificación. Quiero que se pueda hacer clic tanto en el elemento mismo como en la casilla de verificación.

public class MyItem extends ListActivity { 
     private ArrayList<MyItem> items; 
     public void onCreate(Bundle savedInstanceState) { 
      /* code which creates instances of MyItem and inserts them on the *list* variable */ 
     MyArrayAdapter adapter = new MyArrayAdapter(this, R.layout.my_item, list); 

     setListAdapater(adapter); 
     setContentView(R.layout.items_list); 
    } 
     public onListItemClick(ListView l, View v, int position, long id){ 
      //handles the click on an item 
     } 

    public class MyArrayAdapter extends ArrayAdapter<MyItem>{ 
     private MyItem item; 
     public MyArrayAdapter(Context context, int resourceId, ArrayList<MyItem> list){ 
      //code for the constructor 
     } 
     public getView(int position, View convertView, ViewGroup parent){ 
      convertView = inflater.inflate(resourceId, null); 


      this.item = list.get(position); 
      if (this.item == null) { 
       return convertView; 
      } 
      else{ 
       if (resourceId == R.layout.my_item) { 
        final CheckBox cb = (CheckBox)convertView.findViewById(R.id.checkbox); 

        if(cb != null){ 
         //initially 
         if(chosen) 
          cb.setChecked(true); 
         else 
          cb.setChecked(false); 
         //set listener 
         cb.setOnClickListener(new View.OnClickListener() { 
          @Override 
          public void onClick(View arg0) { 
           if(cb.isChecked()) 
            chosen = true; 
           else 
            chosen = false; 
          } 
         }); 
        } 
       } 
      return convertView; 
     } 
    }  
} 

No se preocupe por la variable elegida. Lo escribí para simplemente el código. En realidad, corresponde a un valor en una base de datos. Hacer clic en un elemento funciona bien. Sin embargo, cuando hago clic en una casilla de verificación lo que sucede es lo siguiente:

  • la casilla en la que he hecho clic aparece seleccionada (esto es el trabajo de la interfaz de usuario del Android)
  • la casilla de verificación que internamente se comprueba es el último en la pantalla sea la que sea, es decir, si mi pantalla muestra 8 elementos y hago clic en uno de ellos (no importa cuál), el cheque aparece en la casilla de verificación correcta pero internamente, el 8º elemento es el que se verifica.

Agradecería cualquier ayuda que pudiera proporcionarme. Gracias por adelantado.

Respuesta

6

En realidad, la implementación de respaldo chosen es la clave. Android realiza algunas optimizaciones con vistas de lista para permitirle reutilizar las vistas de los elementos de la lista para evitar la creación excesiva de objetos y la recolección de elementos no utilizados (lo que a menudo generaría un desplazamiento brusco). Como tal, debes asegurarte de que siempre que sea relevante, sepas exactamente con qué elemento de la lista estás trabajando.

Digamos que tiene 100 elementos de la lista. Es probable que su pantalla no pueda mostrarlos todos. Puede mostrar solo diez elementos a la vez. Entonces se crean 10 vistas (realmente ver jerarquías) para mostrar esos elementos visibles. Cuando se desplaza hacia abajo a los siguientes diez elementos, en lugar de crear 10 nuevas vistas (para un total de 20), la lista solo puede crear una más (para cubrir el caso donde la mitad de un elemento se muestra en la parte superior y la mitad de uno) se muestra en la parte inferior de la pantalla para un total de 11 elementos visibles en la pantalla) y el resto de los elementos reutilizan las vistas creadas anteriormente.

Así una mesa conceptual que representa la primera pantalla podría tener este aspecto:

 
Item  View 
------- -------- 
Item 1 View 1 
Item 2 View 2 
Item 3 View 3 
Item 4 View 4 
Item 5 View 5 
Item 6 View 6 
Item 7 View 7 
Item 8 View 8 
Item 9 View 9 
Item 10 View 10 

Y para después desplazarse hacia abajo diez elementos, al principio puede parecer un poco como este (probablemente no exactamente, pero esto le consigue la idea):

 
Item  View 
------- -------- 
Item 11 View 1 
Item 12 View 2 
Item 13 View 3 
Item 14 View 4 
Item 15 View 5 
Item 16 View 6 
Item 17 View 7 
Item 18 View 8 
Item 19 View 9 
Item 20 View 10 

Lo que puede deducir de esto es que una sola vista determinada puede representar diferentes elementos a medida que se desplaza. Esto significa que sus controladores de eventos tienen que ser un poco más dinámicos en la forma en que encuentran el elemento con el que están relacionados.

Todo esto es para darle un poco de historia para que pueda cambiar la forma en que está implementando su método getView. Aquí está su problema real: la variable item está en el alcance de su Adapter. Desafortunadamente, supongo que su código que no ha publicado aquí y que ha reemplazado con chosen usa item. Establece item cada vez que se crea una vista de elemento. Esto significa que después de que se creen esas primeras 8 vistas, item se configura como el 8º elemento en su lista. Cada vez que hace clic en una casilla de verificación, está usando item, que es el 8º elemento y no el elemento que corresponde a la vista de elemento de la lista en la que hizo clic.

Aquí está la estructura para getView que yo recomiendo:

public getView(int position, View convertView, ViewGroup parent){ 
     View view = convertView; 
     if (view == null) { 
      view = inflater.inflate(R.layout.my_item, null); 
     } 

     final MyItem item = list.get(position); 
     final CheckBox cb = (CheckBox)convertView.findViewById(R.id.checkbox); 
     // This stores a reference to the actual item in the checkbox 
     cb.setTag(item); 

     if(item.chosen) 
      cb.setChecked(true); 
     else 
      cb.setChecked(false); 

     //set listener 
     cb.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View view) { 
       // This gets the correct item to work with. 
       final MyItem clickedItem = (MyItem) view.getTag(); 
       if(cb.isChecked()) 
        clickedItem.chosen = true; 
       else 
        clickedItem.chosen = false; 
      } 
     }); 

     return view; 
    } 
} 

Tenga en cuenta que me he librado del nivel de clase item variable.

+0

funcionó como un encanto salvo por un detalle. El setTag/getTag tuvo que ser hecho no a la vista, sino a la casilla de verificación. Gracias, muchas gracias: D –

+1

Actualizado para corregir el error que usted indicó, solo en caso de que alguien más encuentre este código. – kabuko

0

Debe guardar el valor comprobado en su objeto MyItem y el conjunto explícito marcado en la casilla de verificación en el método getView de su adaptador. Use enCheckedChangedListener.

+0

Hago eso. Estoy usando OnClickListener porque estaba intentando con OnCheckedChangeListener pero no funcionaba, así que probé el onClick. El resultado fue el mismo. Mi problema no es guardar los cambios (guarda el cambio en el 8vo elemento de la lista). Mi problema es que dice que estoy haciendo clic en el día 8 cuando estoy haciendo clic en el primero o lo que sea. –

0

Si sigo su explicación, parece que tiene una lista y cada fila en la lista tiene una casilla de verificación y una Vista de texto. En mi aplicación tengo algo similar y, en lugar de intentar hacer clic tanto en la casilla de verificación como en la vista de texto, y reacciono ante eso, utilizo onItemclick de ListView. Cuando ve que el usuario presiona un elemento en la lista, en el código puede marcar o desmarcar la casilla de verificación. Da la impresión visual de que el usuario puede marcar la casilla, pero es súper fácil de implementar.

+0

No entendí bien tu respuesta, pero la de @kabuko resolvió el problema. Gracias de todos modos :) –

2

probar este

public getView(int position, View convertView, ViewGroup parent){ 
    View view = convertView; 
    if (view == null) { 
     view = inflater.inflate(R.layout.my_item, null); 
    } 

    final MyItem item = list.get(position); 
    // This stores a reference to the actual item in the view 
    view.setTag(item); 

    final CheckBox cb = (CheckBox)convertView.findViewById(R.id.checkbox); 

    //set listener 
    cb.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View view) { 
      // This gets the correct item to work with. 
      final MyItem clickedItem = (MyItem) view.getTag(); 
      if(cb.isChecked()) 
       clickedItem.chosen = true; 
      else 
       clickedItem.chosen = false; 
     } 
    }); 


    cb.setChecked(item.chosen); 
    return view; 
} 
+0

+1 solución genial y simple – Simmant

+0

¿Por qué no clickedItem.chosen = cb.isChecked()? – cyroxis

Cuestiones relacionadas