Proyecto: App de Lemonade

1. Antes de comenzar

En este codelab, se presenta una nueva app llamada Lemonade, que compilarás por tu cuenta. En este codelab, seguirás los pasos necesarios para completar el proyecto, incluidas la configuración y las pruebas en Android Studio.

Este codelab es diferente de los demás en este curso. A diferencia de los codelabs anteriores, el propósito de este no es proporcionar un instructivo paso a paso para compilar una app. En cambio, sirve para configurar un proyecto que completarás de forma independiente, y te proporcionará instrucciones para completar una app y controlar tu trabajo por tu cuenta.

En lugar de código de solución, proporcionamos un conjunto de pruebas como parte de la app que descargarás. Debes ejecutar estas pruebas en Android Studio (más adelante en este codelab, te mostraremos cómo hacerlo) y verás si se aprueba el código. Esto puede demorar algunos intentos; incluso los desarrolladores profesionales rara vez pasan todas las pruebas en el primer intento. Una vez que el código haya pasado todas las pruebas, podrás considerar que este proyecto está completo.

Entendemos que posiblemente solo quieras ver la solución para comparar los resultados. No proporcionamos el código de la solución porque queremos que practiques cómo es ser un desarrollador profesional. Para ello, podría ser necesario que uses diferentes habilidades con las que aún no tienes mucha práctica, como las siguientes:

  • Googlear términos, mensajes de error y bits de código de la app que no reconozcas
  • Probar código, leer errores, realizar cambios en el código y volver a probarlo
  • Retomar el contenido anterior de la Unidad 1 de Aspectos básicos de Android para repasar lo que aprendiste
  • Comparar el código que sabes que funciona (es decir, el que se proporciona en el proyecto o el código de solución anterior de otras apps en la Unidad 1) con el que escribes

Esto puede parecer abrumador al principio, pero estamos 100% seguros de que, si pudiste completar la Unidad 1, ya estás listo para este proyecto. Tómate tu tiempo y no te rindas. ¡Puedes lograrlo!

Requisitos previos

  • Este proyecto es para usuarios que completaron la unidad 1 del curso Aspectos básicos de Android en Kotlin.

Qué compilarás

  • Compilarás una app de Lemonade simple usando las habilidades que aprendiste en la unidad 1.

Requisitos

  • Una computadora que tenga Android Studio instalado

2. Descripción general de la app

Te damos la bienvenida al proyecto app de Lemonade.

Te reclutamos en nuestro equipo para que nos ayudes a hacer realidad nuestra visión de crear limonada digital. El objetivo es crear una app para dispositivos móviles interactiva y sencilla que te permita exprimir limones hasta obtener un vaso de limonada. Puedes considerarla metáfora o tal vez solo una manera divertida de pasar el tiempo.

La app de Lemonade estará compuesta por una sola pantalla. Cuando los usuarios inician la app por primera vez, recibirán un saludo con un mensaje en el que pueden elegir un limón si presionan la imagen de un limonero.

1ce5b75b513d63c9.png

Cuando el usuario presiona el limonero, se le presenta un limón que puede presionar para "exprimir" durante una cantidad de veces no especificada (se genera de forma aleatoria la cantidad exacta de presiones necesarias) antes de pasar a la siguiente pantalla.

fb63b41d58a83af7.png

Cuando el usuario exprima el limón la cantidad correcta de veces, verá una imagen de un vaso para "beber" la limonada.

f8882c1688a0e3e7.png

Después de hacer clic para beber la limonada, se vacía el vaso, y el usuario puede presionar la imagen nuevamente para regresar a la primera pantalla y seleccionar otro limonero.

951918f0c2d0464.png

La app tiene un funcionamiento muy simple y se organiza en torno a una sola actividad. Se representan los diferentes estados de la app (cuando el usuario selecciona un limón, lo exprime, bebe la limonada y, finalmente, el vaso vacío) con un objeto denominado máquina de estados. Parece un término teórico sofisticado, pero solo indica que se determina el estado de la app (es decir, el texto y la imagen que se muestran al usuario) mediante una variable que lo contiene (select, squeeze, etc.). Se actualiza el estado de la app, junto con cualquier otra variable necesaria, y luego se configura la IU (la imagen y el texto) por separado, una vez que se realizan todas las actualizaciones.

Todas las variables del estado de la app están predefinidas. Tu trabajo es compilar el diseño de la app e implementar la lógica para que se realicen según lo esperado las transiciones de la IU entre cada estado.

Cómo probar tu código

Para la app de Lemonade (y los futuros proyectos), se te proporcionarán algunas pruebas automatizadas que puedes usar cuando quieras verificar si tu código funciona como según lo esperado.

¿Qué son exactamente las pruebas automatizadas? En el desarrollo de software, se suele considerar a las “pruebas” como códigos que verifican que funcione otro código. Esto se logra comprobando los resultados (como el contenido de los elementos de la IU en la pantalla) para ver si tienen sentido en función de las entradas, conocidas como "casos de prueba". El proyecto inicial para la app de Lemonade incluye algunas pruebas que podrás ejecutar para asegurarte de haber implementado correctamente la lógica. Analizaremos las pruebas en mayor profundidad más adelante. Por ahora, es momento de descargar el código de partida y comenzar a compilar la app de Lemonade.

3. Cómo comenzar

Descarga el código del proyecto

Ten en cuenta que el nombre de la carpeta es android-basics-kotlin-lemonade-app. Selecciona esta carpeta cuando abras el proyecto en Android Studio.

Para obtener el código necesario para este codelab y abrirlo en Android Studio, haz lo siguiente:

Obtén el código

  1. Haz clic en la URL proporcionada. Se abrirá la página de GitHub del proyecto en un navegador.
  2. Verifica y confirma que el nombre de la rama coincida con el nombre de la rama que se especifica en el codelab. Por ejemplo, en la siguiente captura de pantalla, el nombre de la rama es main.

8cf29fa81a862adb.png

  1. En esa página, haz clic en el botón Code, que abre una ventana emergente.

1debcf330fd04c7b.png

  1. En la ventana emergente, haz clic en el botón Download ZIP para guardar el proyecto en tu computadora. 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 nueva con los archivos del proyecto.

Abre el proyecto en Android Studio

  1. Inicia Android Studio.
  2. En la ventana Welcome to Android Studio, haz clic en Open.

d8e9dbdeafe9038a.png

Nota: Si Android Studio ya está abierto, selecciona la opción de menú File > Open.

8d1fda7396afe8e5.png

  1. En el navegador de archivos, ve hasta donde se encuentra la carpeta del proyecto descomprimida (probablemente en Descargas).
  2. Haz doble clic en la carpeta del proyecto.
  3. Espera a que Android Studio abra el proyecto.
  4. Haz clic en el botón Run 8de56cba7583251f.png para compilar y ejecutar la app. Asegúrate de que funcione como se espera.

Tómate un momento para familiarizarte con el proyecto inicial. Presta especial atención al archivo MainActivity.kt.

4181c13884715771.png

En el archivo MainActivity.kt, encontrarás diversas variables para representar el estado actual de la app. Las usarás en un paso posterior para que la aplicación sea interactiva. Si bien la cantidad de código que aparece aquí puede parecer abrumadora, no tendrás que modificar ninguna parte del código que no esté marcada con el mensaje TODO. Encontrarás instrucciones específicas en las páginas siguientes.

También verás que el proyecto también incluye otro paquete, com.example.lemonade (androidTest).

a0c593c77b323c15.png

Este paquete incluye las pruebas automatizadas que usarás para verificar si la funcionalidad que implementas en MainActivity.kt es correcta. También hablaremos de eso en otro momento. Por ahora, tienes todo listo para comenzar a compilar tu app, comenzando por la interfaz de usuario.

4. Compila la interfaz de usuario

La app de Lemonade solo requiere un diseño básico, de modo que necesitas únicamente dos vistas para implementar toda su funcionalidad.

  1. Un objeto TextView que proporciona instrucciones al usuario.
  2. Un objeto ImageView que muestra un gráfico basado en el estado actual de la app (p. ej., un limón que se debe exprimir).

Compilarás este diseño en el archivo activity_main.xml.

554c5e1ae9ec2e42.png

Con los conocimientos que tienes sobre el editor de diseño, tu objetivo es crear un diseño similar al siguiente, con ambas vistas centradas en la pantalla y el elemento TextView arriba del elemento ImageView.

54581304e678827c.png

5. Haz que la app sea interactiva

Cuando completes tu diseño, abre el archivo MainActivity.kt. Aquí implementarás toda la lógica de la app. Notarás que ya hay bastante código. También hay muchos comentarios marcados con // TODO: (se muestra un ejemplo a continuación). Estas son las tareas que debes completar.

b6c5858a42dec80.png

Tendrás que implementar tres tareas básicas para que la app de limonada funcione.

  1. Configura la ImageView de lemonImage para responder a las entradas del usuario.
  2. Implementa el método clickLemonImage() para actualizar el estado de la app.
  3. Implementa el método setViewElements() para actualizar la IU según el estado actual de la app.

Echemos un vistazo a cada una de las tareas.

Paso 1: configura el objeto ImageView

Cuando presionas la vista de imagen, la aplicación debería pasar de un estado a otro. Ten en cuenta que, al final de onCreate(), hay dos objetos de escucha para configurar.

  1. Con setOnClickListener() se debería actualizar el estado de la app. El método para hacerlo es clickLemonImage().
  2. setOnLongClickListener() responde a eventos en los que el usuario mantiene presionada una imagen (p. ej., cuando presiona la imagen y no suelta inmediatamente el dedo). En estos casos, aparece un widget, llamado barra de notificaciones, en la parte inferior de la pantalla, que le informa al usuario la cantidad de veces que exprimió el limón. Para ello, se usa el método showSnackbar().

b07b78c6b607e94d.png

En el siguiente paso, implementarás la lógica para cambiar el estado de la app.

Paso 2: Implementa el método clickLemonImage()

Después de completar el paso anterior, se llama al método clickLemonImage() cada vez que el usuario presiona la imagen. Este método se encarga de cambiar el estado actual de la app por el siguiente y de actualizar las variables según sea necesario. Hay cuatro estados posibles: SELECT, SQUEEZE, DRINK y RESTART. Se representa el estado actual con la variable lemonadeState. Este método debe realizar una acción diferente para cada estado.

  1. SELECT: realiza la transición al estado SQUEEZE y establece el objeto lemonSize (la cantidad de acciones necesarias) llamando al método pick() y configurando el objeto squeezeCount (la cantidad de veces que el usuario exprimió el limón) como 0.
  2. SQUEEZE: Aumenta la squeezeCount de a 1 y disminuye la lemonSize de a 1. Recuerda que un limón requiere una cantidad variable de presiones antes de que la app pueda cambiar su estado. Haz la transición al estado DRINK solo si el nuevo valor de lemonSize es igual a 0. De lo contrario, la app debe permanecer en el estado SQUEEZE.
  3. DRINK: realiza la transición al estado RESTART y establece lemonSize en -1.
  4. RESTART: vuelve al estado SELECT.

Una vez que hayas manejado todas las actualizaciones y transiciones entre estados, asegúrate de llamar al método setViewElements() para actualizar la IU según el nuevo estado.

Paso 3: implementa el método setViewElements()

El método setViewElements() se encarga de actualizar la IU según el estado de la app. Se deben actualizar el texto y la imagen con los valores que se muestran a continuación para que coincidan con el lemonadeState.

SELECT:

  • Texto: Haz clic para seleccionar un limón.
  • Imagen: R.drawable.lemon_tree

SQUEEZE:

  • Texto: Haz clic para exprimir el limón.
  • Imagen: R.drawable.lemon_squeeze

DRINK:

  • Texto: Haz clic para beber la limonada.
  • Imagen: R.drawable.lemon_drink

RESTART:

  • Texto: Haz clic para comenzar de nuevo.
  • Imagen: R.drawable.lemon_restart

Cómo usar recursos de strings

En Android, casi todo es un recurso. Definir recursos a los que luego puedes acceder en tu app es una parte esencial del desarrollo de Android.

Los recursos se usan para todo tipo de tareas, como definir colores, imágenes, diseños, menús y valores de strings. La ventaja es que nada está codificado. Todo está definido en esos archivos de recursos y, luego, se puede hacer referencia a lo que necesites dentro del código de tu aplicación. El más simple de estos recursos (y el más común) es el uso de recursos de strings para permitir un texto flexible y localizado.

Las strings o el texto estático se pueden almacenar en un archivo separado llamado strings.xml en la subcarpeta values de la carpeta res.

b65ed762eea87f2f.png

Para cada texto que desees mostrar en tu aplicación (es decir, la etiqueta de un botón o el texto dentro de una TextView), primero debes definir el texto en el archivo res/values/strings.xml. Cada entrada es una clave (que representa el ID del texto) y un valor (el texto en sí). Por ejemplo, si quieres que un botón muestre "Enviar", agrega el siguiente recurso de strings a res/values/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello!</string>
    <string name="submit_label">Submit</string>
</resources>

Para acceder al recurso directamente en tu código, usa los métodos getResources.getString() o getString() a fin de acceder al valor dado el ID de recurso, R.string.submit_label:

val submitText = getResources().getString(R.string.submit_label)

Para establecer el texto del recurso de strings directamente en TextView, puedes llamar a setText() en el objeto TextView y pasar el ID de recurso.

val infoTextView: TextView = findViewById(R.id.info_textview)

infoTextView.setText(R.string.info_text)

Los recursos de strings también pueden contener caracteres especiales para darle formato al texto. Por ejemplo, puedes tener un recurso de string que te permita insertar otro texto en la string.

<string name="ingredient_tablespoon">%1$d tbsp of %2$s</string>

En el código, puedes acceder a los recursos de strings y darles formato mediante el paso de argumentos.

getResources().getString(R.string.ingredient_tablespoon, 2, "cocoa powder")

Cuando declaras el recurso de strings, cada argumento está numerado en el orden en que aparece (1, 2, etc.) y tiene una letra para identificar el tipo (d para el número decimal, s para string, etc.). Los argumentos del tipo correcto se pueden pasar a la llamada a getString().

2 tbsp of cocoa powder

Para obtener más información, consulta la documentación sobre recursos de strings.

6. Ejecuta tu app

Después de compilar la IU de la app y de implementar la actividad principal, es hora de ver el trabajo en acción. Ejecuta la app usando el menú Run > Run 'app'. Se iniciará el emulador.

1bb3bc95726dde79.png

Ahora la app debería ser totalmente interactiva y deberías poder presionar la vista de imagen para pasar de un estado a otro.

42feefe9ebcf879c.png

Mientras se ve el limón en la pantalla, también puedes intentar mantener presionado el objeto ImageView para ver en la parte inferior la barra de notificaciones, que muestra la cantidad total de veces que se exprimió. Tómate un tiempo para probar todos los estados de la app varias veces. Luego, tómate un momento para felicitarte por tu arduo trabajo.

7. Instrucciones para pruebas

Prueba tu app

Terminaste de implementar la app de Lemonade, pero en el desarrollo profesional de software, escribir código no es casi nunca el último paso. Además del código de la aplicación, las apps de calidad profesional también incluyen código de prueba, que se ejecuta para verificar que el código funcione según lo esperado y que los cambios en el código no introduzcan errores nuevos, un proceso que se denomina prueba automatizada. Si bien no se enseñan las pruebas automatizadas en este proyecto, la app de Lemonade se incluye con algunas pruebas para ayudarte a verificar que hayas implementado correctamente el proyecto. Puedes usarlas como una forma de autoevaluación para ver si cumpliste con todos los requisitos del proyecto y si debes hacer algún cambio en tu app.

¿Qué es exactamente una "prueba"? Las pruebas son fragmentos de código que se incluyen en el proyecto de Android Studio. Ejecutan parte del código de la app y pueden "aprobar" o "fallar", según el comportamiento del código de la app.

¿Dónde puedes encontrar y ejecutar las pruebas de tu app? Las pruebas para la app de Lemonade se encuentran en el objetivo de prueba. Un objetivo es un término que se usa en el desarrollo de software para hacer referencia a un grupo de clases agrupadas. Por ejemplo, la app de Lemonade se encuentra en un objetivo llamado "app", mientras que las pruebas se encuentran en un objetivo llamado "LemonadeTests". Si bien el objetivo de LemonadeTests puede acceder al código desde el objetivo de la app, estos son completamente independientes, y el código de la app no contiene ninguno de los códigos de prueba.

55f884303707e1c3.png

Cuando visualices los archivos en la vista "Android", el nombre del objetivo de prueba será el mismo que el del paquete que la app, pero con (androidTest) entre paréntesis.

También hay algunos términos clave que debes conocer en relación con el código de prueba.

  • Conjunto de pruebas: el objetivo que incluye todos los casos de prueba.
  • Caso de prueba: una clase que consta de pruebas individuales para la funcionalidad relacionada (la app de Lemonade tenía un solo caso de prueba, pero las apps más grandes suelen tener muchos más).
  • Prueba: una función que prueba un elemento específico.

Un caso de prueba puede tener varias pruebas, mientras que el conjunto de pruebas del proyecto puede tener varias.

Cómo realizar las pruebas

Para ejecutar las pruebas, puedes realizar una de las siguientes acciones:

Si se trata de un solo caso de prueba, abre una clase de caso de prueba y haz clic en la flecha verde que está a la izquierda de la declaración de la clase. Luego, puedes seleccionar la opción Run en el menú. Se ejecutarán todas las pruebas del caso de prueba.

6c7b133bcc38ce87.png

Lo más probable es que quieras ejecutar una sola prueba, por ejemplo, si hay una sola prueba fallida y las otras aprueban. Puedes ejecutar una sola prueba como lo harías con todo el caso de prueba. Usa la flecha verde y selecciona la opción Run.

59690e06230bf1e4.png

Si tienes varios casos de prueba, también podrás ejecutar todo el conjunto. Al igual que cuando ejecutas la app, encontrarás esta opción en el menú Run.

ed1e07d2488ac446.png

Ten en cuenta que Android Studio usará, de forma predeterminada, el último objetivo que ejecutaste (app, objetivos de prueba, etc.), por lo que, si el menú sigue indicando Run > Run 'app', podrás ejecutar el objetivo de prueba seleccionando Run > Run.

13bd962d134241aa.png

Luego, selecciona el objetivo de prueba en el menú emergente.

903c7ed01fa7cebf.png

Se muestran los resultados de la ejecución de las pruebas en la pestaña Run. En el panel de la izquierda, verás una lista de las pruebas fallidas, si hay. Se marcan las pruebas fallidas con un signo de exclamación rojo junto al nombre de la función, mientras que las que aprueban tienen una marca de verificación verde.

37c73c4a656622af.png

Si una prueba falla, la salida de texto proporcionará información que te ayudará a solucionar el problema.

92f3c8219c03651d.png

Por ejemplo, en el mensaje de error anterior, la prueba verifica si un objeto TextView usa un recurso de strings específico. Sin embargo, la prueba falla. El texto que sigue a "Esperado" y el que sigue a "Go" no coinciden, lo que significa que el valor que la prueba esperaba no coincide con el valor de la app en ejecución. En este ejemplo, la string que se utiliza en el objeto TextView no es squeeze_count, como espera la prueba.

8. Opcional: Comparte tu app

Cuando termines de disfrutar los vasos de limonada, toma una captura de tu pantalla favorita y compártela en Twitter para mostrar lo que aprendiste. Agrega la etiqueta @AndroidDev y el hashtag #AndroidBasics.