El secreto de los SparseBooleanArray

Cuando desarrollamos una aplicación Android que utiliza listas de elementos (y ¿qué aplicación que se precie no lo hace?), una de las funciones más recurrentes de las que queremos dotar a nuestra lista es de la capacidad de seleccionar los ítems que deseemos para poder realizar otras funciones con ellos. Seleccionarlos y que, por supuesto, al hacer scroll y volver a generar las vistas, la selección permanezca y no se cambie aleatoriamente. En este tutorial vamos a aprender a realizar una selección de los ítems de un RecyclerView para su posterior procesado, y para ello utilizaremos un SparseBooleanArray (conjunto de valores booleanos dispersos) junto con un recurso de tipo selector para marcar el estado seleccionado del ítem.
RecyclerView con varios ítems seleccionados (en azul)

RecyclerView con varios ítems seleccionados (en azul)

Paso a paso

La selección de ítems consiste básicamente en ejecutar el método setSelected(true) sobre una vista cuando hacemos clic sobre ella, cuando marcamos su CheckBox o de cualquier otra manera que se nos pueda ocurrir. Para el usuario, un ítem se percibe como seleccionado cuando destaca de alguna manera frente a los demás (para lo que utilizaremos un selector —un archivo xml que contendrá los colores del ítem según su estado). Además, tendremos que guardar un valor que nos indique cuál es este ítem para recuperarlo cuando llegue el momento de trabajar con él (para lo cual se emplea el SparseBooleanArray). Estos son los pasos que seguiremos para implementar una selección múltiple
  1. Definir un layout para nuestra Activity
  2. Crear un selector
  3. Crear un layout para cada ítem del RecyclerView
  4. Crear una clase de prueba
  5. Crear un adaptador para el RecyclerView
  6. Definir la Activity
Este tutorial se divide en 6 pasos. Si solo quieres ver cómo implementar el selector, cómo se seleccionar los elementos o cómo recuperar los objetos, puedes ver solamente los pasos 2, 5 y 6 respectivamente.

1. Definir el layout para nuestra Activity

Comenzaremos creando un RecyclerView en la MainActivity. Para ello lo primero es importar la biblioteca necesaria añadiendo la siguiente dependencia a nuestro build.gradle: compile 'com.android.support:recyclerview-v7:22.2.0' Es conveniente que la versión de la biblioteca sea la misma que la de nuestra biblioteca Support: compile 'com.android.support:appcompat-v7:22.2.0' Ahora ya podemos definir nuestro layout de la siguiente manera: activity_main_layout.xml Hemos puesto un botón y un campo de texto que nos servirán para mostrar después cuáles son los ítems marcados.

2. Crear un selector

Un selector es un archivo de recursos xml que contiene la definición de estados y un recurso asociado a cada uno. Cuando establezcamos este selector como valor de la propiedad background de una vista, el background de esta vista tendrá el valor del ítem que se haya definido en el selector del para el estado en el que se encuentre la vista. En nuestro caso vamos a definir un color para cada uno de los tres estados que nos interesan: uno para el estado normal, uno para cuando se haga clic sobre el ítem y otro para cuando el ítem esté seleccionado. Debemos definir los colores previamente en el archivo res/values/colors.xml, ya que no nos permite incluir el código hexadecimal del color directamente en el selector: colors.xml selector.xml Como vemos, para el estado presionado (state_pressed) hemos definido el color presionado, para el estado seleccionado (state_selected) hemos definido el color seleccionado y para el estado por defecto (sin atributo), el color normal. Existe una gran cantidad de estados para los cuales podemos definir colores o recursos drawable.

3. Crear un layout para cada ítem del RecyclerView

Vamos a definir un layout bastante simple para cada elemento del RecyclerView. Contendrá únicamente un texto. item_layout.xml Como podemos observar, hemos puesto el selector en la propiedad background de la vista del elemento raíz, de forma que el fondo del ítem cambiará de color ante sus cambios de estado. Es importante que las vistas que contiene el layout no tengan color de fondo, o si no, al seleccionar el ítem, este color permanecerá, resultando en un efecto poco agradable.
Diseño de ítem: texto de color naranja sobre un fondo de color rojo

Diseño del ítem

4. Crear una clase de prueba

Para poblar el RecyclerView vamos a crear la clase ObjetoSimple, que solo contenga un String. En el paso 6 crearemos una colección de datos de prueba en la Activity.

 5. Crear un adaptador para el RecyclerView

Será en este adaptador donde definiremos el comportamiento de la selección de ítems. Básicamente, lo que haremos será establecer un onClickListener para el elemento raíz del ítem de forma que, al hacer clic sobre este (si está activado el modo selección), se guarde su posición en un SparseBooleanArray y así tener una referencia que no desaparezca al destruirse el ítem. Presta especial atención al método bindView() y a los listener. El selector que le hemos puesto al LinearLayout del item_layout.xml detectará por sí mismo cuándo el ítem es clicado, y se pintará del color indicado en el selector. Así pues, lo más simple, que es hacer que el ítem cambie de color al hacer clic sobre él ya está hecho. Ahora vamos a la selección. El meollo de la cuestión está en el método bindView() y en el uso del SparseBooleanArray. Esto es a grosso modo lo que sucederá: cuando hagamos un clic largo en uno de los ítems de la lista, este se seleccionará y se entrará en el modo selección. Desde ese momento, podremos seleccionar y deseleccionar ítems haciendo clic sobre ellos. El modo selección finaliza cuando hayamos deseleccionado todos los elementos. Podemos ver cuáles son los ítems seleccionados pulsando el botón superior Seleccionados. ¿Cómo se logra esto? El modo selección se activa mediante un booleano que ponemos a true o a false según convenga, y que, si está activo, al hacer clic en un ítem, se guardará un par valor número-booleano en el SparseBooleanArray indicando que esa posición del adaptador está seleccionada o deseleccionada. Veámoslo sobre el código. Al hacer un clic largo en uno de los ítems, se activa el modo selección y se marca el ítem como seleccionado. Para ello solo tenemos que poner el atributo modoSeleccion a true y usar el método put() del SparseBooleanArray, al cual le pasaremos la posición del ítem en el adaptador y el valor true. De esta manera el SparseBooleanArray contendrá una posición y un valor que indicará si está seleccionada o no. Vamos a continuación al principio del método bindView(). Cada vez que se llame al método bindView(), es decir, cada vez que se genere la vista del ítem, se comprobará si en el SparseBooleanArray figura un valor de true para la posición que se está creando. Si está, la vista se seleccionará. Si, por el contrario, figura false o no figura nada (devolverá false igualmente), se deseleccionará. Al ejecutar una de estas dos líneas, el selector coloreará la vista del color seleccionado o del color normal respectivamente. El ítem hace referencia a la vista padre del item_layout.xml, es decir, al LinearLayout, ya que la hemos inflado en el método onCreateViwHolder(): View v = LayoutInflater.from(context).inflate(R.layout.item_layout, parent, false); Inflar significa que la vista v va a convertirse en la vista padre del layout, que le pasemos. Por este motivo, al poner un listener sobre item, lo estamos poniendo sobre el LinearLayout de item_layout.xml y dado que este es el que tiene el selector en su atributo background, todo encaja. Lo siguiente es saber cómo seleccionar ítems.
RecyclerView con ítem pulsado de color azul

Ítem presionado. Al hacer clic se colorea de azul y desaparece cuando dejamos de pulsarlo.

RecyclerView con distintos ítems seleccionados en un color azul distinto al anterior

Ítems seleccionados. Al entrar en modo selección, los ítems permanecen pulsados.

En este trozo de código estamos asignando un listener al ítem. Este listener hace lo siguiente: si el modo de selección está activado, comprueba si la vista está seleccionada. Si no está seleccionada, se seleccionará y se guardará su posición y el valor booleano true en el SparseBooleanArray para marcar el ítem como seleccionado cuando se vuelva a generar la vista. El selector detectará que está seleccionada y coloreará la vista. Si está seleccionada, se deseleccionará, se almacenará el valor false para la posición del ítem para marcarlo como deseleccionado cuando se vuelva a generar el ítem, el selector coloreará de nuevo el ítem del color correspondiente y además se comprobará si quedan más ítems seleccionados. Esto último se hace con el método haySeleccionados(), el cual recorrerá el conjunto de datos del adaptador y comprobará por cada posición si está marcada o no. En el momento en que haya una marcada, devolverá true, indicando que hay al menos un ítem seleccionado. Si no hubiera ninguno seleccionado, se desactivaría el modo selección y entonces, no podríamos volver a seleccionar ítems hacendo clic hasta que lo volviésemos a activarlo haciendo un clic largo en alguno de los ítems. De igual manera que estamos cambiando el color al seleccionar los ítems, podríamos cambiar cualquier de sus características. Podríamos mostrar botones o imágenes ocultas, sustituir el texto por otro, cambiar su tamaño, su fuente…

 6. Definir la Activity

Por último, este es el código de la MainActivity. MainActivity.java Cuando pulsemos el botón btn_obtener se hará una llamada al método obtenerSeleccionados() del adaptador, el cual recorre todas las posiciones de la colección comprobando si para cada una de ellas hay un valor true en el SparseBooleanArray. Para aquellas posiciones con el valor true, se guarda el objeto ObjetoSimple de la colección datos del adaptador en una LinkedList y cuando los haya comprobado todos, los devuelve. Una vez obtenidos, podemos hacer lo que queramos con nuestros objetos, ya sea modificarlos y actualizarlos en nuestra base de datos, borrarlos o simplemente mostrarlos como es el caso. Ahora, podemos marcar cualquier elemento de la lista sin que la selección cambie de ítem al hacer scroll en el RecyclerView.
RecyclerView con ítems seleccionados en azul y un texto indicando cuáles son los ítems marcados.

Al pulsar el botón, obtenemos los objetos seleccionados y mostramos su contenido.

8 Comentarios
  1. Mario German 8 años

    Hola Como puedo crear un reciclerview en el cual sea tomado desde dos diferentes botones (Laboratorio y Clínica) la idea es que cada botón me envíe una información diferente como debería hacerlo.

    Te agradezco la ayuda. Gracias

  2. Autor
    Juan José Melero 8 años

    Perdón por no haber visto el comentario antes. Lo has resuelto ya o aún quieres que te mande código de ejemplo?

    • jesus 8 años

      mandamelo a mi porfa lo de ottobus , recycler view y volley que se caayeron los proyectos
      nvillasanchez@gmail.com

      • Autor

        Esta tarde buscaré dónde tengo los proyectos y restauraré los enlaces para que los veas. Gracias por avisar, Jesús.

  3. Joseph 7 años

    Buenas Tardes,
    por favor , podria mandarme el codigo que utiliza?

  4. Jack 5 años

    Buenos dias…
    Tomando este ejemplo, como mantener seleccionados los elementos marcados (ejem: Selecciono dos items) luego de ir a otro fragment y volver a este… al volver a este debe seguir seleccionado los elementos

    • Autor

      En el caso de que tu activity no sea destruida (es decir, que puedas volver a ella pulsando «Atrás»), no hay que hacer nada, porque el contenido de esta no es destruido. Pero si es destruida (porque cierras la activity), según tu propósito, tienes que guardar los elementos seleccionados en memoria (una clase singleton) o en el dispositivo (una base de datos, o un fichero de texto) y luego recuperarlos. Tienes que guardar algo que te permita identificar los elementos que estaban marcados para, en el momento de cargar los elementos, consultar cuáles estaban marcados y volver a ponerlos marcados en el onBindView().

  5. Emmanuel 4 años

    Me gustaria probar el código, trabajo en un chat
    Se puede descargar?

Responder a Mario German Haga clic aquí para cancelar la respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

©2024 Codictados Comunidad libre para el aprendizaje de codigo Online

o

Inicia Sesión con tu Usuario y Contraseña

o    

¿Olvidó sus datos?

o

Create Account