Condicionales en Kotlin (parte 2)

En este codelab, agregarás imágenes de dados a tu app existente de Dice Roller para Android. Primero, asegúrate de completar el codelab anterior sobre la compilación base de la app de Dice Roller.

En lugar de mostrar el valor del lanzamiento del dado en un elemento TextView, tu app mostrará la imagen del dado apropiada según la cantidad de caras que tenga el dado que se lance. Esto brindará a tu app una experiencia del usuario mucho más visual y mejorada.

Se te proporcionará un vínculo para que descargues las imágenes de dados, que agregarás como recursos en tu app. Para escribir el código de la imagen que se usará, deberás utilizar una sentencia when en Kotlin.

Prerequisites

  • Haber completado el codelab Cómo crear un app de Dice Roller para Android con un botón
  • Capacidad de escribir sentencias de flujo de control (sentencias if / else, when)
  • Capacidad de actualizar la IU de la app según la entrada del usuario (modificando el archivo MainActivity.kt)
  • Capacidad de agregar un objeto de escucha de clics a un Button.
  • Capacidad de agregar recursos de imagen a una app para Android

Qué aprenderás

  • Cómo actualizar un elemento ImageView mientras la app se está ejecutando.
  • Cómo personalizar el comportamiento de tu app según las diferentes condiciones (mediante una sentencia when).

Qué compilarás

  • Compilarás la app para Android de Dice Roller que tiene un Button para lanzar un dado y actualizar la imagen que se muestra en la pantalla.

Requisitos

  • Una computadora que tenga Android Studio instalado
  • Conexión a Internet para descargar las imágenes de los dados

En esta tarea, reemplazarás el elemento TextView de tu diseño por un elemento ImageView que muestre una imagen del resultado del lanzamiento del dado.

Cómo abrir la app de Dice Roller

  1. Abre y ejecuta la app de Dice Roller en la sección Cómo crear una app de Dice Roller para Android con un botón en Android Studio.
    La app debería verse de la siguiente manera:

  1. Abre activity_main.xml (app > res > layout > activity_main.xml).
    Se abrirá el editor de diseño.

Cómo borrar el elemento TextView

  1. En el editor de diseño, selecciona el elemento TextView en Component Tree.
  1. Haz clic con el botón derecho y selecciona Borrar o presiona la tecla Delete.
  2. Por el momento, ignora la advertencia en el Button. Deberás corregir eso en el siguiente paso.

Cómo agregar un elemento ImageView al diseño

  1. Arrastra un elemento ImageView de Palette hasta la vista Design y colócalo sobre el Button.

  1. En el diálogo Pick a Resource, selecciona avatars en Sample data. Esta es la imagen temporal que usarás hasta que agregues las imágenes de dados en la próxima tarea.

  1. Presiona OK. La vista Design de tu app debería verse de la siguiente manera:

  1. En Component Tree, verás dos errores. El elemento Button no tiene restricciones verticales, y el elemento ImageView, tampoco tiene restricciones verticales ni horizontales.

El Button no tiene restricciones verticales porque quitaste el elemento TextView debajo del que se encontraba en un principio. Ahora deberás colocar el elemento ImageView y el Button debajo de este.

Cómo posicionar los elementos ImageView y Button

Deberás centrar el elemento ImageView de forma vertical en la pantalla, independientemente de dónde se ubique el Button.

  1. Agrega restricciones horizontales a ImageView. Conecta el lado izquierdo del elemento ImageView al borde izquierdo del ConstraintLayout superior.
  2. Conecta el lado derecho de ImageView al borde derecho del elemento superior.
    Esto centrará horizontalmente el elemento ImageView dentro del elemento superior.

  1. Agrega una restricción vertical a ImageView conectando la parte superior de la ImageView a la del elemento superior.
    El ImageView se deslizará hacia arriba hasta la parte superior del ConstraintLayout.
  2. Agrega una restricción vertical a Button conectando la parte superior del Button a la inferior de ImageView.
    El Button se deslizará hacia arriba hasta quedar debajo de la ImageView.
  3. Vuelve a seleccionar el elemento ImageView y agrega una restricción vertical que conecte la parte inferior de ImageView a la del elemento superior.
    Esta acción centra el elemento ImageView en sentido vertical en el ConstraintLayout.

Deberían desaparecer todas las advertencias relativas a las restricciones.

Después de estos pasos, la vista Design debería verse así: ImageView en el centro y Button justo debajo.

Puede que veas una advertencia en el elemento ImageView de Component Tree que indica que se debe agregar una descripción de contenido a tu ImageView. Por el momento, no te preocupes por esta advertencia, ya que, más adelante en el codelab, establecerás la descripción de contenido de ImageView según la imagen de dado que muestres. Este cambio se realizará en el código Kotlin.

En esta tarea, descargarás algunas imágenes de dados y las agregarás a tu app.

Cómo descargar las imágenes de dados

  1. Abre esta URL para descargar en tu computadora un archivo ZIP con imágenes de dados. Espera a que se complete la descarga.
  2. Ubica el archivo en tu computadora (probablemente en la carpeta Descargas).
  3. Haz doble clic en el archivo ZIP para descomprimirlo. Se creará una carpeta DiceImages nueva con los 6 archivos de imagen de dados, que muestran los valores de dado del 1 al 6.

Cómo agregar imágenes de dados a tu app

  1. En Android Studio, haz clic en View > Tool Windows > Resource Manager en los menús o en la pestaña Resource Manager, a la izquierda de la ventana Project.
  2. Haz clic en + debajo de Resource Manager y selecciona Import Drawables. Se abrirá un navegador de archivos.

  1. Busca y selecciona los 6 archivos de imagen de dados. Puedes seleccionar el primer archivo, mantener presionada la tecla Shift y, luego, seleccionar los otros archivos.
  2. Haz clic en Open.
  1. Haz clic en Next y, luego, en Import para confirmar que deseas importar estos 6 recursos.

  1. Si los archivos se importaron con éxito, las 6 imágenes aparecerán en la lista Drawable de tu app.

¡Buen trabajo! En la próxima tarea, usarás estas imágenes en tu app.

Importante: Podrás consultar estas imágenes en tu código Kotlin mediante sus ID de recurso:

  • R.drawable.dice_1
  • R.drawable.dice_2
  • R.drawable.dice_3
  • R.drawable.dice_4
  • R.drawable.dice_5
  • R.drawable.dice_6

Cómo reemplazar la imagen de avatar de ejemplo

  1. En Design Editor, selecciona el elemento ImageView.
  2. En Attributes, en la sección Declared Attributes, busca el atributo srcCompat de la herramienta, que se establece como la imagen de avatar.

Recuerda que el atributo srcCompat de la herramienta usa la imagen proporcionada solo dentro de la vista Design de Android Studio. La imagen solo se muestra a los desarrolladores mientras compilas la app, pero no se verá cuando la ejecutes en el emulador o en un dispositivo.

  1. Haz clic en la pequeña vista previa del avatar. Se abrirá un cuadro de diálogo a fin de seleccionar un nuevo recurso para que uses con esta ImageView.

  1. Selecciona el elemento de diseño dice_1 y haz clic en OK.

¡Vaya! El elemento ImageView ocupa toda la pantalla.

A continuación, ajusta el ancho y la altura del elemento ImageView de modo que no oculte el Button.

  1. En la ventana Attributes debajo del Constraints Widget, busca los atributos layout_width y layout_height. Su configuración actual es wrap_content, lo que significa que el elemento ImageView será tan alto y tan ancho como el contenido (la imagen de origen) dentro del mismo.
  2. En su lugar, establece un ancho fijo de 160 dp y una altura fija de 200 dp en ImageView. Presiona Enter.

    Ahora, ImageView es mucho más pequeño.


Es posible que el Button esté demasiado cerca de la imagen.

  1. Agrega un margen superior de 16 dp al botón. Configúralo en el Constraint Widget.

Cuando se actualice la vista Design, la app se verá mucho mejor.

Cómo cambiar la imagen del dado cuando se hace clic en el botón

Aunque se corrigió el diseño, debes actualizar la clase MainActivity para usar las imágenes de dados.

En este momento, hay un error en la app, en el archivo MainActivity.kt. Si intentas ejecutarla, verás el siguiente error de compilación:

Esto se debe a que tu código todavía hace referencia al elemento TextView que borraste del diseño.

  1. Abre MainActivity.kt (app > java > com.example.diceroller > MainActivity.kt).

El código hace referencia al elemento R.id.textView, pero Android Studio no lo reconoce.

  1. Dentro del método rollDice(), selecciona las porciones de código que hagan referencia al elemento TextView y bórralas.
// Update the TextView with the dice roll
val resultTextView: TextView = findViewByID(R.id.textView)
resultTextView.text = dice.roll().toString()
  1. Aún dentro de rollRice(), crea una nueva variable llamada diceImage de tipo ImageView. Configúrala igual que el elemento ImageView del diseño. Usa el método findViewById() y pasa el ID de recurso para ImageView, R.id.imageView, como argumento de entrada.
val diceImage: ImageView = findViewById(R.id.imageView)

Si no sabes cómo determinar el ID de recurso exacto de ImageView, verifica el id en la parte superior de la ventana Attributes.

Cuando te refieras a este ID de recurso en el código Kotlin, asegúrate de escribirlo de forma exacta (con i minúscula, V mayúscula y sin espacios). De lo contrario, Android Studio mostrará un error.

  1. Agrega esta línea de código para probar que puedas actualizar correctamente el elemento ImageView cuando se hace clic en el botón. El lanzamiento del dado no siempre será "2", pero usa la imagen dice_2 para hacer una prueba.
diceImage.setImageResource(R.drawable.dice_2)

Este código llama al método setImageResource() de ImageView y pasa el ID de recurso de la imagen dice_2. Se actualizará el elemento ImageView en pantalla a fin de que muestre la imagen dice_2.

El método rollDice() ahora debería verse así:

private fun rollDice() {
    val dice = Dice(6)
    val diceRoll = dice.roll()
    val diceImage: ImageView = findViewById(R.id.imageView)
    diceImage.setImageResource(R.drawable.dice_2)
}
  1. Ejecuta tu app para verificar que no se produzcan errores.

La app debe iniciarse con una pantalla en blanco que solo contenga el botón Roll.

Cuando presiones el botón, aparecerá una imagen de dados que mostrará el valor 2. ¡Eso es!

Pudiste hacer que la imagen cambie luego de presionar el botón. ¡Estás cada vez más cerca!

Claramente, el resultado del lanzamiento del dado no siempre será un 2. Usa la lógica de flujo de control que aprendiste en el codelab Cómo agregar comportamiento condicional para diferentes lanzamientos de dados a fin de que en la pantalla se muestre la imagen de dado apropiada según el lanzamiento aleatorio.

Antes de comenzar a escribir código, piensa de manera conceptual en cómo se comportará la app y escribe pseudocódigo que describa lo que debería suceder. Por ejemplo:

Si el usuario lanza un 1, mostrar la imagen dice_1.

Si el usuario lanza un 2, mostrar la imagen dice_2.

Y así sucesivamente.

El pseudocódigo anterior se puede escribir con sentencias if / else en Kotlin según el valor del dado.

if (diceRoll == 1) {
   diceImage.setImageResource(R.drawable.dice_1)
} else if (diceRoll == 2) {
   diceImage.setImageResource(R.drawable.dice_2)
}
 ...

Sin embargo, escribir if / else para cada caso resulta bastante repetitivo. Se puede expresar la misma lógica de una forma más simple, con una sentencia when. Esto resulta más conciso (requiere menos código). Usa este enfoque en tu aplicación.

when (diceRoll) {
   1 -> diceImage.setImageResource(R.drawable.dice_1)
   2 -> diceImage.setImageResource(R.drawable.dice_2)
   ...

Cómo actualizar el método rollDice()

  1. En el método rollDice(), borra la línea de código que establece el ID de recurso de la imagen siempre en dice_2.
diceImage.setImageResource(R.drawable.dice_2)
  1. Reemplázala por una sentencia when que actualice el elemento ImageView según el valor de diceRoll.
   when (diceRoll) {
       1 -> diceImage.setImageResource(R.drawable.dice_1)
       2 -> diceImage.setImageResource(R.drawable.dice_2)
       3 -> diceImage.setImageResource(R.drawable.dice_3)
       4 -> diceImage.setImageResource(R.drawable.dice_4)
       5 -> diceImage.setImageResource(R.drawable.dice_5)
       6 -> diceImage.setImageResource(R.drawable.dice_6)
   }

Cuando termines de aplicar los cambios, el método rollDice() debería verse de la siguiente manera:

private fun rollDice() {
   val dice = Dice(6)
   val diceRoll = dice.roll()

   val diceImage: ImageView = findViewById(R.id.imageView)

   when (diceRoll) {
       1 -> diceImage.setImageResource(R.drawable.dice_1)
       2 -> diceImage.setImageResource(R.drawable.dice_2)
       3 -> diceImage.setImageResource(R.drawable.dice_3)
       4 -> diceImage.setImageResource(R.drawable.dice_4)
       5 -> diceImage.setImageResource(R.drawable.dice_5)
       6 -> diceImage.setImageResource(R.drawable.dice_6)
   }
}
  1. Ejecuta la app. Si haces clic en el botón Roll, la imagen del dado cambiará y mostrará un valor distinto de 2. ¡Funciona!

Cómo optimizar tu código

Si deseas escribir un código aún más conciso, puedes hacer el cambio que se muestra a continuación. No tiene ningún efecto visible para el usuario de tu app, pero el código será más corto y menos repetitivo.

Quizá notaste que la llamada a diceImage.setImageResource() aparece 6 veces en tu sentencia when.

when (diceRoll) {
    1 -> diceImage.setImageResource(R.drawable.dice_1)
    2 -> diceImage.setImageResource(R.drawable.dice_2)
    3 -> diceImage.setImageResource(R.drawable.dice_3)
    4 -> diceImage.setImageResource(R.drawable.dice_4)
    5 -> diceImage.setImageResource(R.drawable.dice_5)
    6 -> diceImage.setImageResource(R.drawable.dice_6)
}

Lo único que cambia entre cada caso es el ID de recurso utilizado. Esto significa que puedes crear una variable para almacenar el ID de recurso que se usará. Luego, podrás llamar a diceImage.setImageResource() una sola vez en tu código y pasar el ID de recurso correcto.

  1. Reemplaza el código anterior con el siguiente:
val drawableResource = when (diceRoll) {
   1 -> R.drawable.dice_1
   2 -> R.drawable.dice_2
   3 -> R.drawable.dice_3
   4 -> R.drawable.dice_4
   5 -> R.drawable.dice_5
   6 -> R.drawable.dice_6
}

diceImage.setImageResource(drawableResource)

Un concepto nuevo aquí es que una expresión when en realidad puede mostrar un valor. Con este nuevo fragmento de código, la expresión when muestra el ID de recurso correcto, que se almacenará en la variable drawableResource. Podrás usar esa variable para actualizar el recurso de imagen que se mostrará.

  1. Ten en cuenta que when ahora está subrayada en color rojo. Si colocas el cursor sobre ella, verás un mensaje de error: la expresión "when" debe estar completa, agrega la rama "else" necesaria.

El error se debe a que el valor de la expresión when se asigna a drawableResource, por lo que when debe ser exhaustivo: debe manejar todos los casos posibles de modo que siempre se muestre un valor, incluso si cambias a un dado de 12 caras. Android Studio sugiere agregar una rama else. Para solucionar este problema, cambia el caso de 6 a else. Los casos del 1 al 5 son los mismos, pero else administra todos los demás, incluido 6.

val drawableResource = when (diceRoll) {
   1 -> R.drawable.dice_1
   2 -> R.drawable.dice_2
   3 -> R.drawable.dice_3
   4 -> R.drawable.dice_4
   5 -> R.drawable.dice_5
   else -> R.drawable.dice_6
}

diceImage.setImageResource(drawableResource)
  1. Ejecuta la app para comprobar que siga funcionando correctamente. Asegúrate de hacer suficientes pruebas a fin de asegurarte de que todos los números aparezcan con las imágenes de dado del 1 al 6.

Cómo establecer una descripción de contenido apropiada en el elemento ImageView

Ahora que reemplazaste el número lanzado por una imagen, los lectores de pantalla ya no podrán determinar qué número salió. Para solucionar este problema, después de actualizar el recurso de imagen, actualiza la descripción del contenido del elemento ImageView. Esta debe ser una descripción de texto de lo que se muestra en el elemento ImageView a fin de que los lectores de pantalla puedan describirlo.

diceImage.contentDescription = diceRoll.toString()

Los lectores de pantalla pueden leer en voz alta esta descripción de contenido. Por lo tanto, si en la pantalla se muestra la imagen del valor "6", la descripción de contenido se leerá en voz alta como "6".

Cómo crear una experiencia de lanzamiento más útil

Cuando el usuario abre la app por primera vez, esta está en blanco (solo contiene el botón Roll), lo cual se ve extraño. Como es posible que los usuarios no sepan qué esperar, cambia la IU a fin de que muestre un lanzamiento de dado aleatorio cuando se inicie la app por primera vez y se cree el elemento Activity. De esta forma, es más probable que los usuarios comprendan que, si presionan el botón Roll, se generará un lanzamiento del dado.

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)

   val rollButton: Button = findViewById(R.id.button)
   rollButton.setOnClickListener { rollDice() }

   // Do a dice roll when the app starts
   rollDice()
}

Cómo agregar comentarios a tu código

Agrega algunos comentarios al código para describir lo que sucederá en virtud del código que escribiste.

Después de realizar todos estos cambios, así es cómo podría verse tu método rollDice():

/**
* Roll the dice and update the screen with the result.
*/
private fun rollDice() {
   // Create new Dice object with 6 sides and roll the dice
   val dice = Dice(6)
   val diceRoll = dice.roll()

   // Find the ImageView in the layout
   val diceImage: ImageView = findViewById(R.id.imageView)

   // Determine which drawable resource ID to use based on the dice roll
   val drawableResource = when (diceRoll) {
       1 -> R.drawable.dice_1
       2 -> R.drawable.dice_2
       3 -> R.drawable.dice_3
       4 -> R.drawable.dice_4
       5 -> R.drawable.dice_5
       else -> R.drawable.dice_6
   }

   // Update the ImageView with the correct drawable resource ID
   diceImage.setImageResource(drawableResource)

   // Update the content description
   diceImage.contentDescription = diceRoll.toString()
}

Para obtener el archivo MainActivity.kt completo, consulta el código de la solución en GitHub cuyo vínculo aparece a continuación.

Te felicitamos por completar la app de Dice Roller. ¡Ya puedes llevar esta app a la próxima noche de juegos con tus amigos!

El código de la solución para este codelab se encuentra en el proyecto y módulo que se muestran a continuación.

A fin de obtener el código de este codelab desde GitHub y abrirlo en Android Studio, haz lo siguiente:

  1. Inicia Android Studio.
  2. En la ventana Welcome to Android Studio, haz clic en Check out project from version control.
  3. Elige Git.

  1. En el cuadro de diálogo Clone Repository, pega la URL del código proporcionado en el cuadro URL.
  2. Haz clic en el botón Test, espera y asegúrate de que haya un cuadro emergente de color verde que diga Connection successful.
  3. De manera opcional, cambia el Directory a uno diferente al predeterminado sugerido.

  1. Haz clic en Clone. Android Studio comenzará a recuperar tu código.
  2. En la ventana emergente Checkout from Version Control, haz clic en Yes.

  1. Espera a que se abra Android Studio.
  2. Selecciona el módulo correcto para tu inicio del codelab o código de la solución.

  1. Haz clic en el botón Run para compilar y ejecutar el código.
  • Usa setImageResource() para cambiar la imagen que se muestra en un elemento ImageView.
  • Usa sentencias de flujo de control, como expresiones if / else o when, a fin de manejar diferentes casos en tu app, por ejemplo, para mostrar imágenes distintas en circunstancias diferentes.

Haz lo siguiente:

  1. Agrega otro dado a la app, de modo que un botón Roll muestre 2 resultados. ¿Cuántos ImageViews necesitarás en tu diseño? ¿Cómo afectará eso al código MainActivity.kt?

Revisa tu trabajo

La app terminada debería ejecutarse sin errores y mostrar los dos dados.