Cómo crear una experiencia del usuario más prolija

Como viste en los codelabs anteriores, Material es un sistema de diseño creado por Google con lineamientos, componentes y herramientas que respaldan las prácticas recomendadas para el diseño de interfaces de usuario. En este codelab, actualizarás la app de la calculadora de propinas (de los codelabs anteriores) para que ofrezca una experiencia del usuario más prolija, como se muestra en la siguiente captura de pantalla final. También probarás la app en algunas situaciones adicionales para garantizar que la experiencia del usuario sea lo más fluida posible.

5743ac5ee2493d7.png

Requisitos previos

  • Conocer los conceptos básicos de los widgets de IU comunes, como TextView, ImageView, Button, EditText, RadioButton, RadioGroup y Switch
  • Conocer los conceptos básicos de ConstraintLayout y el posicionamiento las vistas secundarias estableciendo restricciones
  • Sentirse a gusto con la modificación de diseños XML
  • Conocer la diferencia entre las imágenes de mapas de bits y las interfaces dibujables en vector
  • Poder establecer atributos de tema en un tema
  • Poder activar el Tema oscuro en un dispositivo
  • Haber modificado anteriormente el archivo build.gradle de la app para las dependencias del proyecto

Qué aprenderás

  • Cómo usar los componentes de Material Design en tu app
  • Cómo importar los íconos de material desde Image Asset Studio para usarlos en tu app
  • Cómo crear y aplicar nuevos estilos
  • Cómo configurar otros atributos de tema además del color

Qué compilarás

  • Una app para calcular propinas prolija que sigue las prácticas recomendadas de la IU

Requisitos

  • Una computadora que tenga Android Studio instalado
  • El código de la app de Tip Time después de completar los codelabs anteriores

En los codelabs anteriores, creaste la app Tip Time, una app de calculadora de propinas que ofrece opciones para personalizar la propina. Actualmente, la IU de tu app se ve como la siguiente captura de pantalla. Las funciones se ejecutan correctamente, pero se parece más a un prototipo. Los campos no están alineados visualmente. Definitivamente, se pueden realizar mejoras en términos de estilo y espaciado más coherentes, así como con el uso de componentes de Material Design.

6685eaafba30960a.png

Los componentes de Material son widgets de IU comunes que facilitan la implementación de los estilos de Material en tu app. En la documentación, se proporciona información sobre cómo usar y personalizar los componentes de Material Design. Hay lineamientoss generales de Material Design para cada componente y una guía específica de la plataforma de Android para los componentes disponibles en Android. Los diagramas etiquetados te proporcionan suficiente información para recrear un componente en caso de que no exista en la plataforma que elegiste.

c4a4db857bb36c3f.png

Si usas los componentes de Material, tu app funcionará de manera más uniforme junto con otras apps en el dispositivo del usuario. De esta manera, los patrones de la IU aprendidos en una app pueden transferirse a la siguiente. Por lo tanto, los usuarios podrán aprender a usar tu app mucho más rápido. Se recomienda a usar los componentes de Material siempre que sea posible (en lugar de los widgets que no son de Material). También son más flexibles y personalizables, como aprenderás en la próxima tarea.

La biblioteca de componentes de Material Design (MDC) debe incluirse como una dependencia en tu proyecto. Si usas Android Studio 4.1 o versiones posteriores, esta línea ya debería estar presente en tu proyecto de forma predeterminada. En el archivo build.gradle de tu app, asegúrate de que esta dependencia se incluya en la versión más reciente de la biblioteca. Para obtener más detalles, consulta la página Comenzar del sitio de Material.

app/build.gradle

dependencies {
    ...
    implementation 'com.google.android.material:material:<version>'
}

Campos de texto

En tu app de calculadora de propinas, en la parte superior del diseño, actualmente tienes un campo EditText para los costos de servicio. Este campo EditText funciona, pero no sigue los lineamientos recientes de Material Design sobre cómo se ven y se comportan los campos de texto.

Comienza por aprender sobre cualquier componente nuevo que desees utilizar en el sitio de Material. En la guía sobre Campos de texto, hay dos tipos de campos de texto:

Campo de texto completado

bea54a560820fe84.png

Campo de texto con contorno

c37af7d70aad8aa6.png

Para crear un campo de texto como se muestra más arriba, usa un TextInputLayout con un TextInputEditText adjunto de la biblioteca MDC. El campo de texto de Material se puede personalizar fácilmente para lo siguiente:

  • Mostrar texto de entrada o una etiqueta que siempre está visible
  • Mostrar un ícono en el campo de texto
  • Mostrar mensajes de error o del asistente

En la primera tarea de este codelab, reemplazarás el costo de servicio EditText por un campo de texto de Material (que está compuesto por un TextInputLayout y un TextInputEditText).

  1. Con la app Tip Time abierta en Android Studio, ve al archivo de diseño activity_main.xml. Debería contener un ConstraintLayout con el diseño de la calculadora de propinas.
  2. Para ver un ejemplo de la apariencia del XML para un campo de texto de Material, vuelve a la guía de Android sobre campos de texto. Deberías ver fragmentos como este:
<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/textField"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="@string/label">

    <com.google.android.material.textfield.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
    />

</com.google.android.material.textfield.TextInputLayout>
  1. Después de ver este ejemplo, inserta un campo de texto de material como el primer elemento secundario de ConstraintLayout (antes del campo EditText). Quitarás el campo EditText en un paso posterior.

Puedes escribir esto en Android Studio y usar el autocompletado para facilitar este proceso. También puedes copiar el archivo XML de ejemplo de la página de documentación y pegarlo en tu diseño de esta manera. Observa cómo TextInputLayout tiene una vista secundaria, TextInputEditText. Recuerda que la elipsis (...) se usa para abreviar fragmentos, de modo que puedas centrarte en las líneas del XML que realmente cambiaron.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    ...>

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/textField"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/label">

        <com.google.android.material.textfield.TextInputEditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
        />

    </com.google.android.material.textfield.TextInputLayout>

    <EditText
        android:id="@+id/cost_of_service" ... />

    ...

Se espera que veas errores en el elemento TextInputLayout. Aún no restringiste correctamente esta vista en la ConstraintLayout superior. Tampoco se reconoce el recurso de strings. Solucionarás estos errores en los próximos pasos.

1cf3f25cc047f291.png

  1. Agrega restricciones verticales y horizontales al campo de texto para ubicarlo correctamente en el elemento ConstraintLayout superior. Como aún no borraste el EditText, corta y pega los siguientes atributos de EditText y colócalos en TextInputLayout: las restricciones, el ID de recurso cost_of_service, el ancho del diseño de 160dp, la altura del diseño de wrap_content y el texto de la sugerencia @string/cost_of_service.
...

<com.google.android.material.textfield.TextInputLayout
   android:id="@+id/cost_of_service"
   android:layout_width="160dp"
   android:layout_height="wrap_content"
   android:hint="@string/cost_of_service"
   app:layout_constraintStart_toStartOf="parent"
   app:layout_constraintTop_toTopOf="parent">

   <com.google.android.material.textfield.TextInputEditText
       android:layout_width="match_parent"
       android:layout_height="wrap_content"/>

</com.google.android.material.textfield.TextInputLayout>

...

Es posible que veas un error que indica que el ID de cost_of_service es igual al ID de recurso de EditText, pero puedes ignorar este error por el momento. (EditText se quitará en algunos pasos).

  1. A continuación, asegúrate de que el elemento TextInputEditText tenga todos los atributos adecuados. Corta y pega el tipo de entrada de EditText en TextInputEditText. y cambia el ID de recurso del elemento TextInputEditText a cost_of_service_edit_text..
<com.google.android.material.textfield.TextInputLayout ... >

   <com.google.android.material.textfield.TextInputEditText
       android:id="@+id/cost_of_service_edit_text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="numberDecimal" />

</com.google.android.material.textfield.TextInputLayout>

El ancho de match_parent y la altura de wrap_content son correctos. Cuando se define un ancho de match_parent, el TextInputEditText tiene el mismo ancho que su elemento superior TextInputLayout, que es 160dp.

  1. Ahora que copiaste toda la información relevante de EditText, borra el elemento EditText del diseño.
  2. En la vista Design de tu diseño, debería aparecer esta vista previa. El campo de costo de servicio ahora se parece al campo de texto de Material.

148df54f0deda630.png

  1. Aún no puedes ejecutar la app porque hay un error en tu archivo MainActivity.kt en el método calculateTip(). Recuerda de un codelab anterior que con la vinculación de vista habilitada para tu proyecto, Android crea propiedades en un objeto de vinculación basadas en el nombre del ID de recurso. El campo sobre el cual recuperamos el costo de servicio cambió en el diseño XML, por lo que el código Kotlin se debe actualizar en consecuencia.

Ahora, recuperarás la entrada del usuario del elemento TextInputEditText con el ID de recurso cost_of_service_edit_text. En MainActivity, usa binding.costOfServiceEditText para acceder a la string de texto almacenada en ella. El resto del método calculateTip() puede mantenerse igual.

private fun calculateTip() {
    // Get the decimal value from the cost of service text field
    val stringInTextField = binding.costOfServiceEditText.text.toString()
    val cost = stringInTextField.toDoubleOrNull()

    ...
}
  1. ¡Muy bien! Ahora ejecuta la app y comprueba que aún funcione. Observa que la etiqueta "Cost of Service" ahora aparecerá encima de tus datos a medida que escribes. La sugerencia se debe calcular como se espera.

b4a27e58f63417b7.png

Interruptores

En los lineamientos de Material Design, también encontrarás orientación sobre los interrupciones. Un botón es un widget en el que puedes activar o desactivar una configuración.

  1. Consulta la guía de Android sobre los interruptores de Material. Aprenderás sobre el widget SwitchMaterial (de la biblioteca MDC), que proporciona estilos de Material para interruptores. Si sigues desplazándote por la guía, verás algunos ejemplos de XML.
  2. Para usar SwitchMaterial, debes especificar SwitchMaterial de forma explícita en tu diseño y usar el nombre de ruta de acceso completamente calificado.

En el diseño de activity_main.xml, cambia la etiqueta XML de Switch a com.google.android.material.switchmaterial.SwitchMaterial..

...

<com.google.android.material.switchmaterial.SwitchMaterial
    android:id="@+id/round_up_switch"
    android:layout_width="0dp"
    android:layout_height="wrap_content" ... />

...
  1. Ejecuta la app para verificar que siga compilando. No ocurre ningún cambio visible en la app. Sin embargo, una ventaja de usar SwitchMaterial de la biblioteca MDC (en lugar de Switch desde la plataforma de Android) es que, cuando se actualiza la implementación de la biblioteca para SwitchMaterial (p. ej., se modifican los lineamientos de Material Design). recibirás el widget actualizado de forma gratuita sin necesidad de realizar cambios. Esto ayuda a que tu app esté preparada para el futuro.

En este punto, vimos dos ejemplos de cómo la IU puede beneficiarse de usar componentes de Material Design listos para usar y cómo mejora tu app de acuerdo con los lineamientos de Material. Recuerda que siempre puedes explorar otros componentes de Material Design que se proporcionan en Android en este sitio.

Los íconos son símbolos que ayudan a los usuarios a comprender la interfaz de usuario comunicando de forma visual la función prevista. Suelen inspirarse en los objetos del mundo físico que se espera que haya experimentado un usuario. Por lo general, el diseño del ícono reduce el nivel de detalle al mínimo necesario para que le resulte familiar al usuario. Por ejemplo, un lápiz en el mundo físico se usa para escribirlo, de modo que el equivalente de ícono generalmente indica crear, agregar o editar un elemento.

Foto de Angelina Litvin en Unsplash

A veces, los íconos están vinculados a objetos de mundo físico obsoletos, como es el caso del ícono del disquete. Este ícono es la indicación aproximada de guardar un archivo o registro de base de datos. Sin embargo, aunque los disquetes se popularizaron en la década de 1970, dejaron de ser comunes después de 2000. Sin embargo, el hecho de que se siga usando en la actualidad muestra cómo un elemento visual fuerte puede trascender la vida útil de su forma física.

Foto de Vincent Botta en Unsplash

Cómo representar íconos de tu app

En el caso de los íconos de tu app, en lugar de proporcionar diferentes versiones de una imagen de mapa de bits para diferentes densidades de pantalla, se recomienda usar elementos de diseño vectoriales. Los elementos de diseño vectoriales se representan como archivos XML que almacenan las instrucciones sobre cómo crear una imagen en lugar de guardar los píxeles reales que conforman esa imagen. Los elementos de diseño vectoriales pueden aumentarse o reducirse sin que se pierda la calidad visual ni el tamaño del archivo.

Íconos proporcionados

Material Design proporciona una serie de íconos organizados en categorías comunes para la mayoría de tus necesidades. Consulta la lista de íconos.

76e9b6c4ec0cbbe6.png

Estos íconos también se pueden dibujar con uno de cinco temas (Filled, Outlined, Rounded, Two-Tone y Sharp) y se pueden agregar a tonos con colores.

Filled

Outlined

Rounded

Two-Tone

Sharp

Cómo agregar íconos

En esta tarea, agregarás tres íconos de elementos de diseño vectoriales a la app:

  1. Ícono junto al campo de texto de costo de servicio
  2. Ícono junto a la pregunta de servicio
  3. Ícono junto a la solicitud de redondeo

A continuación, se muestra una captura de pantalla de la versión final de la app. Una vez que agregues los íconos, modificarás el diseño para que se ajuste a su ubicación. Observa cómo los campos y el botón Calcular se desplazan hacia la derecha, con la incorporación de los íconos.

8c4225390dd1fb20.png

Cómo agregar elementos de la interfaz dibujable en vector

Puedes crear estos íconos como elementos de diseño vectoriales directamente desde Asset Studio en Android Studio.

  1. Abre la pestaña Resource Manager ubicada a la izquierda de la ventana de la aplicación.
  2. Haz clic en el ícono + y selecciona Vector Asset.

6a692157a2ada3f6.png

  1. En Asset Type, asegúrate de que el botón de selección etiquetado Clip Art esté seleccionado.

698ab1c8dc2d1714.png

  1. Haz clic en el botón junto a Clip Art: para seleccionar otra imagen prediseñada. En el mensaje que aparece, escribe "call made" en la ventana. Utilizarás este ícono de flecha para la opción de redondeo de propina. Selecciónala y haz clic en OK.

50b0008ed6ab8d6d.png

  1. Cambia el nombre del ícono por ic_round_up. (Se recomienda usar el prefijo ic_ cuando nombres los archivos de íconos). Puedes dejar **Size** como 24 dp x 24 dp y **Color** como black 000000.
  2. Presiona Siguiente.
  3. Acepta la ubicación predeterminada del directorio y haz clic en Finish.

9f522a73be34ecf6.png

  1. Repite los pasos 2 a 7 para los otros dos íconos:
  • Ícono de pregunta de servicio: Busca el ícono de "servicio a la habitación" y guárdalo como ic_service.
  • Ícono de costo de servicio: Busca el ícono de "tienda" y guárdalo como ic_store.
  1. Cuando termines, Resource Manager se verá como en la siguiente captura de pantalla. También verás estos tres elementos de diseño vectoriales (ic_round_up, ic_service y ic_store) en tu carpeta res/drawable.

3c895747fbfa3793.png

Compatibilidad con versiones anteriores de Android

Acabas de agregar elementos de diseño vectoriales a tu app, pero es importante tener en cuenta que la compatibilidad con estos elementos en la plataforma de Android no se agregó hasta Android 5.0 (nivel de API 21).

Según la configuración de tu proyecto, la versión mínima del SDK para la app de Tip Time es la API 19. Esto significa que la app puede ejecutarse en dispositivos Android que ejecutan la versión 19 o versiones posteriores de la plataforma de Android.

Para que tu app funcione en estas versiones anteriores de Android (lo que se conoce como retrocompatibilidad), agrega el elemento vectorDrawables al archivo build.gradle de tu app. Esto te permite usar elementos de diseño vectoriales en versiones de la plataforma anteriores a la API 21, en lugar de realizar la conversión a PNG cuando se crea el proyecto. Obtén más detalles aquí.

app/build.gradle

android {
  defaultConfig {
    ...
    vectorDrawables.useSupportLibrary = true
   }
   ...
}

Con tu proyecto configurado correctamente, ahora podrás pasar de agregar los íconos al diseño.

Cómo insertar íconos y posicionar elementos

Usarás ImageViews para mostrar los íconos en la app. Así es como aparecerá tu IU final.

5d970eb04c642544.png

  1. Abre el diseño de activity_main.xml.
  2. Primero, posiciona el ícono de la tienda junto al campo de texto de costo de servicio. Inserta un nuevo objeto ImageView como el primer elemento secundario de ConstraintLayout, antes de TextInputLayout.
<androidx.constraintlayout.widget.ConstraintLayout
   ...>

   <ImageView
       android:layout_width=""
       android:layout_height=""

   <com.google.android.material.textfield.TextInputLayout
       android:id="@+id/cost_of_service"
       ...
  1. Configura los atributos adecuados en ImageView para mantener el ícono de ic_store. Configura el ID como icon_cost_of_service. Establece el atributo app:srcCompat en el recurso de elemento de diseño @drawable/ic_store y obtendrás una vista previa del ícono junto a esa línea de XML. También configura android:importantForAccessibility="no", ya que esta imagen solo se usa con fines decorativos.
<ImageView
    android:id="@+id/icon_cost_of_service"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:importantForAccessibility="no"
    app:srcCompat="@drawable/ic_store" />

Se espera que haya un error en el ImageView, ya que la vista aún no está restringida. A continuación, corregirás este problema.

  1. Coloca el icon_cost_of_service en dos pasos. Primero, agrega restricciones al elemento ImageView (este paso) y, luego, actualiza las restricciones en el TextInputLayout junto a él (paso 5). En este diagrama, se muestra cómo se deben configurar las restricciones.

e23287bdeca07a1e.png

En ImageView, deseas que el borde inicial esté restringido al borde inicial de la vista superior (app:layout_constraintStart_toStartOf="parent").

El ícono aparece centrado verticalmente en comparación con el campo de texto que está al lado, así que restringe la parte superior de este ImageView (layout_constraintTop_toTopOf) a la parte superior del campo de texto. Limita la parte inferior de este ImageView (layout_constraintBottom_toBottomOf) al final del campo de texto. Para hacer referencia al campo de texto, usa el ID de recurso @id/cost_of_service. El comportamiento predeterminado es que cuando dos restricciones se aplican a un widget en la misma dimensión (como una restricción superior y inferior), las restricciones se aplican de la misma manera. El resultado es que el ícono se centre verticalmente en relación con el campo de costo de servicio.

<ImageView
    android:id="@+id/icon_cost_of_service"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:importantForAccessibility="no"
    app:srcCompat="@drawable/ic_store"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@id/cost_of_service"
    app:layout_constraintBottom_toBottomOf="@id/cost_of_service" />

El ícono y el campo de texto aún se superponen en la vista Design. Esto se corregirá en el siguiente paso.

  1. Antes de agregar el ícono, el campo de texto se ubicó al inicio del elemento principal. Ahora se debe desplazar hacia la derecha. Actualiza las restricciones del campo de texto cost_of_service en relación con icon_cost_of_service.

40c0c8f04f53a87d.png

El borde inicial de TextInputLayout debe limitarse al borde final de ImageView (@id/icon_cost_of_service). Para agregar un poco de espacio entre las dos vistas, agrega un margen de inicio de 16dp en el TextInputLayout.

<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/cost_of_service"
    ...
    android:layout_marginStart="16dp"
    app:layout_constraintStart_toEndOf="@id/icon_cost_of_service">

    <com.google.android.material.textfield.TextInputEditText ... />

</com.google.android.material.textfield.TextInputLayout>

Después de todos estos cambios, el ícono debe estar ubicado correctamente junto al campo de texto.

6ca04c3c964d5acc.png

  1. Luego, inserta el ícono de campana de servicio junto al mensaje "¿Cómo fue el servicio?" TextView. Aunque puedes declarar el ImageView en cualquier lugar de ConstraintLayout, tu diseño XML será más fácil de leer si insertas el nuevo ImageView en el diseño XML después de TextInputLayout, pero antes del service_question TextView.

Para la nueva ImageView, asígnale un ID de recurso de @+id/icon_service_question. Establece las restricciones apropiadas en ImageView y la pregunta de servicio TextView.

4487340b399e8105.png

También agrega un margen superior de 16dp a service_question TextView para que haya más espacio vertical entre la pregunta de servicio y el campo de texto de servicio más arriba.

...

   <ImageView
        android:id="@+id/icon_service_question"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:importantForAccessibility="no"
        app:srcCompat="@drawable/ic_service"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/service_question"
        app:layout_constraintBottom_toBottomOf="@id/service_question" />

    <TextView
        android:id="@+id/service_question"
        ...
        android:layout_marginTop="16dp"
        app:layout_constraintStart_toStartOf="@id/cost_of_service"
        app:layout_constraintTop_toBottomOf="@id/cost_of_service"/>

...
  1. En este punto, la vista Design debería verse así. El campo de costo de servicio y la pregunta de servicio (y sus respectivos íconos) se ven geniales, pero los botones de selección ahora salen de la ubicación. No están alineados verticalmente con el contenido anterior.

cdfd16c1851c88eb.png

  1. Mejora la posición de los botones de selección moviéndolos hacia la derecha, debajo de la pregunta de servicio. Eso significa actualizar una restricción RadioGroup. Restringe el borde inicial de RadioGroup al borde inicial de service_question TextView. Todos los demás atributos de RadioGroup pueden permanecer iguales.

58a62fae3d676fe4.png

...

<RadioGroup
    android:id="@+id/tip_options"
    ...
    app:layout_constraintStart_toStartOf="@id/service_question">

...
  1. Para continuar, agrega el ícono ic_round_up al diseño junto al interruptor de la opción de redondeo de propina. Intenta hacer esto por tu cuenta, y si no sabes cómo hacerlo, consulta el siguiente archivo XML. Puedes asignar al nuevo ImageView un ID de recurso de icon_round_up.
  2. En el XML de diseño, inserta una ImageView nueva después del RadioGroup, pero antes del widget SwitchMaterial.
  3. Asigna el ImageView del ID de recurso de icon_round_up y establece srcCompat en el elemento de diseño del ícono @drawable/ic_round_up. Restringe el inicio del ImageView al inicio del elemento principal y también centra el ícono verticalmente con relación al SwitchMaterial.
  4. Actualiza SwitchMaterial para que aparezca junto al ícono y tenga un margen de inicio de 16dp. Así debería verse el XML resultante para icon_round_up y round_up_switch.
...

   <ImageView
        android:id="@+id/icon_round_up"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:importantForAccessibility="no"
        app:srcCompat="@drawable/ic_round_up"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/round_up_switch"
        app:layout_constraintBottom_toBottomOf="@id/round_up_switch" />

    <com.google.android.material.switchmaterial.SwitchMaterial
        android:id="@+id/round_up_switch"
        ...
        android:layout_marginStart="16dp"
        app:layout_constraintStart_toEndOf="@id/icon_round_up" />

...
  1. La vista Design debería verse así. Los tres íconos están bien posicionados.

f4b0e8d61d91986b.png

  1. Si comparas esto con la captura de pantalla final de la app, notarás que el botón de cálculo también se desplaza de manera vertical para alinearlo con el costo de servicio, la pregunta de servicio, las opciones del botón de selección y la pregunta de redondeo de propina. Para lograr esto, restringe el inicio del botón de cálculo al inicio del round_up_switch. Además, agrega un 8dp de margen vertical entre el botón de cálculo y el interruptor que aparece sobre él.

60d5a43819c2367d.png

...

<Button
   android:id="@+id/calculate_button"
   ...
   android:layout_marginTop="8dp"
   app:layout_constraintStart_toStartOf="@id/round_up_switch" />

...
  1. Por último, posiciona tip_result agregando 8dp de margen superior a TextView.

beb333a518b51323.png

...

<TextView
   android:id="@+id/tip_result"
   ...
   android:layout_marginTop="8dp" />

...
  1. Fueron muchos pasos. Felicitaciones por completarlos. Hay que prestar mucha atención a los detalles para que los elementos se alineen correctamente en el diseño, pero hace que el resultado final se vea mucho mejor. Ejecuta la app. Debería verse como en la siguiente captura de pantalla. Los elementos no aparecen amontonados, ya que se los alineó de forma vertical y se aumentó el espacio entre ellos.

1f2ef2c0c9a9bdc7.png

Aún no has terminado. Quizás hayas notado que el tamaño de fuente y el color de la pregunta de servicio y el importe de la propina no coinciden con el texto de los botones de selección y el interruptor. Hagamos que sean coherentes en la siguiente tarea mediante el uso de estilos y temas.

Un estilo es una colección de valores de atributos de vista para un solo tipo de widget. Por ejemplo, un estilo TextView puede especificar el color y el tamaño de fuente, el color de fondo, etc. Al extraer estos atributos en un estilo, puedes aplicar el estilo a varias vistas en el diseño de manera sencilla y mantenerlo en un solo lugar.

En esta tarea, primero crearás estilos para la vista de texto, el botón de selección y el cambio de widgets.

Cómo crear estilos

  1. Crea un archivo nuevo llamado styles.xml en el directorio res > values, si aún no existe. Para crearlo, haz clic con el botón derecho en el directorio values y selecciona New > Values Resource File. Asígnale el nombre styles.xml. El nuevo archivo tendrá el siguiente contenido.
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>
  1. Crea un nuevo estilo TextView para que el texto parezca coherente en toda la app. Define el estilo una vez en styles.xml y luego puedes aplicarlo a todos los TextViews del diseño. Si bien puedes definir un estilo desde cero, puedes extenderte desde un estilo TextView existente de la biblioteca de MDC.

Cuando aplicas estilo a un componente, generalmente debes extender desde un estilo superior del tipo de widget que usas. Esto es importante por dos motivos. Primero, se asegura de que todos los valores predeterminados importantes estén configurados en tu componente y, en segundo lugar, tu estilo seguirá heredando los cambios futuros que pertenezcan a ese estilo superior.

Puedes asignarle un nombre a tu estilo, pero existe una convención recomendada. Si la heredas de un estilo de Material superior, asigna un nombre a tu estilo de forma paralela; para ello, sustituye MaterialComponents por el nombre de tu app (TipTime). Esto traslada los cambios a su propio espacio de nombres, lo que elimina la posibilidad de conflictos futuros cuando los componentes de Material presenten nuevos estilos. Ejemplo:

Nombre de tu estilo: Widget.TipTime.TextView Hereda del estilo superior: Widget.MaterialComponents.TextView

Agrégalo a tu archivo styles.xml entre las etiquetas de apertura y cierre de resources.

<style name="Widget.TipTime.TextView" parent="Widget.MaterialComponents.TextView">
</style>
  1. Configura tu estilo de TextView para que anule los siguientes atributos: android:minHeight,android:gravity, y android:textAppearance.

android:minHeight establece una altura mínima de 48 dp en TextView. La altura más baja de cualquier fila debe ser de 48 dp según los lineamientos de Material Design.

Puedes centrar el texto en TextView verticalmente si configuras el atributo android:gravity. (Consulta la captura de pantalla que aparece a continuación). La gravedad controla cómo se posicionará el contenido dentro de una vista. Como el contenido de texto real no ocupa toda la altura de 48 dp, el valor center_vertical centra el texto dentro de TextView verticalmente (pero no cambia su posición horizontal). Otros valores de gravedad posibles son center, center_horizontal, top y bottom. No dudes en probar los otros valores de gravedad para ver el efecto en el texto.

bd89f5a76d67ada6.png

Establece el valor del atributo de apariencia del texto en ?attr/textAppearanceBody1. TextAppearance es un conjunto de estilos prediseñados para el tamaño del texto, las fuentes y otras propiedades del texto. Para descubrir otras apariencias de texto posibles que proporciona Material, consulta esta lista de los tipos de escala.

<style name="Widget.TipTime.TextView" parent="Widget.MaterialComponents.TextView">
    <item name="android:minHeight">48dp</item>
    <item name="android:gravity">center_vertical</item>
    <item name="android:textAppearance">?attr/textAppearanceBody1</item>
</style>
  1. Para aplicar el estilo de Widget.TipTime.TextView a la TextView de service_question, agrega un atributo de estilo en cada TextView de activity_main.xml.
<TextView
    android:id="@+id/service_question"
    style="@style/Widget.TipTime.TextView"
    ... />

Antes del estilo, TextView se veía así con el tamaño de fuente pequeño y el color de fuente gris:

5cd99583da77efba.png

Después de agregar el estilo, TextView se ve de la siguiente manera: Ahora este TextView se ve más coherente con el resto del diseño.

296a89a6015d9e15.png

  1. Aplica ese mismo estilo Widget.TipTime.TextView a tip_result TextView.
<TextView
    android:id="@+id/tip_result"
    style="@style/Widget.TipTime.TextView"
    ... />

c45860bda6761be7.png

  1. El mismo estilo de texto debe aplicarse a la etiqueta de texto del interruptor. Sin embargo, no puedes establecer un estilo TextView en un widget SwitchMaterial. Los estilos de TextView solo se pueden aplicar en TextViews. Por lo tanto, crea un nuevo estilo para el cambio. Los atributos son los mismos en términos de minHeight, gravity y textAppearance. A continuación, se explica qué es el nombre del estilo y el elemento superior porque ahora se hereda del estilo Switch de la biblioteca MDC. Tu nombre de estilo también debe reflejar el nombre del estilo superior.

Tu nombre de estilo: Widget.TipTime.CompoundButton.Switch. Hereda del estilo superior: Widget.MaterialComponents.CompoundButton.Switch.

<style name="Widget.TipTime.CompoundButton.Switch" parent="Widget.MaterialComponents.CompoundButton.Switch">
   <item name="android:minHeight">48dp</item>
   <item name="android:gravity">center_vertical</item>
   <item name="android:textAppearance">?attr/textAppearanceBody1</item>
</style>

También puedes indicar atributos adicionales específicos de los cambios en este estilo, pero no es necesario que lo hagas en tu app.

  1. El texto del botón de selección es el último lugar donde deseas asegurarte de que el texto aparezca visualmente coherente. No puedes aplicar un estilo TextView o un estilo Switch a un widget RadioButton. En su lugar, debes crear un nuevo estilo para los botones de selección. Puedes extender el estilo RadioButton de la biblioteca de MDC.

Cuando estés creando este estilo, agrega también un relleno entre el texto del botón de selección y la imagen visual del círculo. paddingStart es un nuevo atributo que no usaste aún. El relleno es la cantidad de espacio entre el contenido de una vista y los límites de la vista. El atributo paddingStart establece el relleno solo al comienzo del componente. Consulta la diferencia entre 0 dp y 8 dp de paddingStart en un botón de selección.

e1cef41d95740600.png

25f75f5c36085e76.png

<style name="Widget.TipTime.CompoundButton.RadioButton"
parent="Widget.MaterialComponents.CompoundButton.RadioButton">
   <item name="android:paddingStart">8dp</item>
   <item name="android:textAppearance">?attr/textAppearanceBody1</item>
</style>
  1. (Opcional) Crea un archivo dimens.xml para mejorar la administración de los valores utilizados con frecuencia. Puedes crear el archivo de la misma manera que lo hiciste para el archivo styles.xml anterior. Selecciona el directorio de valores y haz clic con el botón derecho y selecciona New > Values Resource File.

En esta app pequeña, repetiste la configuración de altura mínima dos veces. Eso es manejable por el momento, pero se descontrolaría rápidamente si tuviéramos 4, 6, 10 o más componentes que comparten ese valor. Recordar cambiarlos todos individualmente es tedioso y propenso a errores. Puedes crear otro archivo de recursos útil en res > values llamado dimens.xml que contenga las dimensiones comunes a las que puedas asignar un nombre. Debido a la estandarización de los valores comunes como dimensiones con nombre, facilitamos la administración de nuestra app. Tip Time es pequeña, por lo que no la utilizaremos fuera de este paso opcional. Sin embargo, con apps más complejas en un entorno de producción en el que podrías trabajar con un equipo de diseño, dimens.xml te permitirá cambiar con facilidad esos valores con más frecuencia.

dimens.xml

<resources>
   <dimen name="min_text_height">48dp</dimen>
</resources>

Deberías actualizar el archivo styles.xml para que use @dimen/min_text_height en lugar de 48dp directamente.

...
<style name="Widget.TipTime.TextView" parent="Widget.MaterialComponents.TextView">
    <item name="android:minHeight">@dimen/min_text_height</item>
    <item name="android:gravity">center_vertical</item>
    <item name="android:textAppearance">?attr/textAppearanceBody1</item>
</style>
...

Cómo agregar estos estilos a tus temas

Tal vez hayas notado que aún no aplicaste los nuevos estilos RadioButton y Switch a los widgets respectivos. Esto se debe a que usarás atributos de tema para configurar radioButtonStyle y switchStyle en el tema de la app. Repasemos qué es un tema.

Un tema es una colección de recursos con nombre (llamados atributos de tema) a los que se puede hacer referencia más adelante en estilos, diseños, etc. Puedes especificar un tema para una app, una actividad o una jerarquía de vistas en general, no solo una View. individual. Anteriormente, modificaste el tema de la app en themes.xml mediante la configuración de atributos de tema como colorPrimary y colorSecondary, que se usan en toda la app y sus componentes.

radioButtonStyle y switchStyle son otros atributos de tema que puedes establecer. Los recursos de estilo que proporciones para estos atributos de tema se aplicarán a cada botón de selección y a cada interruptor de la jerarquía de vistas a la que se aplique el tema.

También hay un atributo de tema para textInputStyle, en el que el recurso de estilo especificado se aplicará a todos los campos de entrada de texto dentro de la app. Para que TextInputLayout aparezca como un campo de texto detallado (como se muestra en los lineamientos de Material Design), hay un estilo OutlinedBox definido en la biblioteca de MDC como Widget.MaterialComponents.TextInputLayout.OutlinedBox. Este es el estilo que usarás.

b00a91da56e6f6e2.png

  1. Modifica el archivo themes.xml para que el tema haga referencia a los estilos deseados. La configuración de un atributo de tema se realiza de la misma manera en la que declaraste los atributos de tema colorPrimary y colorSecondary en un codelab anterior. Sin embargo, esta vez, los atributos de tema relevantes son textInputStyle, radioButtonStyle y switchStyle. Usarás los estilos que creaste anteriormente para RadioButton y Switch, junto con el estilo del campo de texto material OutlinedBox.

Copia lo siguiente en res/values/themes.xml en la etiqueta de estilo del tema de tu app.

<item name="textInputStyle">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox</item>
<item name="radioButtonStyle">@style/Widget.TipTime.CompoundButton.RadioButton</item>
<item name="switchStyle">@style/Widget.TipTime.CompoundButton.Switch</item>
  1. Tu archivo res/values/themes.xml debería verse de la siguiente manera. Puedes agregar comentarios en el XML si lo deseas (indicado por <!- y -->).
<resources xmlns:tools="http://schemas.android.com/tools">

    <!-- Base application theme. -->
    <style name="Theme.TipTime" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        ...
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Text input fields -->
        <item name="textInputStyle">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox</item>
        <!-- Radio buttons -->
        <item name="radioButtonStyle">@style/Widget.TipTime.CompoundButton.RadioButton</item>
        <!-- Switches -->
        <item name="switchStyle">@style/Widget.TipTime.CompoundButton.Switch</item>
    </style>

</resources>
  1. Asegúrate de realizar los mismos cambios en el Tema oscuro en themes.xml (night). Tu archivo res/values-night/themes.xml debería tener el siguiente aspecto:
<resources xmlns:tools="http://schemas.android.com/tools">

    <!-- Application theme for dark theme. -->
    <style name="Theme.TipTime" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        ...
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Text input fields -->
        <item name="textInputStyle">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox</item>
        <!-- For radio buttons -->
        <item name="radioButtonStyle">@style/Widget.TipTime.CompoundButton.RadioButton</item>
        <!-- For switches -->
        <item name="switchStyle">@style/Widget.TipTime.CompoundButton.Switch</item>
    </style>

</resources>
  1. Ejecuta la app y observa los cambios. El estilo de OutlinedBox se ve mucho mejor para el campo de texto y ahora todo el texto parece coherente.

31ac15991713b031.png 3e861407146c9ed4.png

Cuando estés cerca de completar tu app, debes probarla no solo con el flujo de trabajo previsto, sino también en otras situaciones de los usuarios. Tal vez descubras que algunos pequeños cambios en el código pueden mejorar la experiencia del usuario de forma considerable.

Cómo rotar el dispositivo

  1. Rota tu dispositivo al modo de paisaje. Primero, es posible que debas habilitar la configuración de Rotación automática. (Se encuentra en la Configuración rápida del dispositivo o en Configuración > Pantalla > Avanzada > Girar la pantalla automáticamente).

f2edb1ae9926d5f1.png

En el emulador, puedes usar sus opciones (ubicadas en la parte superior derecha, junto al dispositivo) para rotar la pantalla hacia la derecha o la izquierda.

da8aee11166adf41.png

  1. Notarás que se truncarán algunos de los componentes de la IU, incluido el botón Calcular. Esto evita que uses la app.

d73499f9c9d2b330.png

  1. Para solucionar este error, agrega un ScrollView alrededor del ConstraintLayout. El código XML se verá de la siguiente manera.
<ScrollView
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_height="match_parent"
   android:layout_width="match_parent">

   <androidx.constraintlayout.widget.ConstraintLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:padding="16dp"
       tools:context=".MainActivity">

       ...
   </ConstraintLayout>

</ScrollView>
  1. Vuelve a ejecutar y probar la app. Cuando giras el dispositivo al modo de paisaje, deberías poder desplazar la IU para acceder al botón Calcular y ver el resultado. Esta corrección no solo es útil en el modo de paisaje, sino también en otros dispositivos Android que pueden tener dimensiones diferentes. Ahora, independientemente del tamaño de la pantalla del dispositivo, el usuario puede desplazarse por el diseño.

Cómo ocultar teclado con la tecla Intro

Es posible que notes que, después de ingresar a un costo de servicio, el teclado permanece activo. Es un poco complicado ocultar manualmente el teclado cada vez para acceder mejor al botón de cálculo. En su lugar, haz que el teclado se oculte automáticamente cuando presionas la tecla Intro.

e2c3a3dbc40218a2.png

Para el campo de texto, puedes definir un objeto de escucha de claves para responder a los eventos cuando se presionan ciertas teclas. Cada opción de entrada posible en un teclado tiene un código de clave asociado, incluida la tecla Enter. Ten en cuenta que un teclado en pantalla también se conoce como un teclado liviano, en lugar de uno físico.

1c95d7406d3847fe.png

En esta tarea, configura un objeto de escucha de claves en el campo de texto para escuchar cuando se presione la tecla Enter. Cuando se detecte ese evento, oculta el teclado.

  1. Copia este método auxiliar y pégalo en tu clase MainActivity. Puedes insertarlo justo antes de la llave de cierre de la clase MainActivity. handleKeyEvent() es una función auxiliar privada que oculta el teclado en pantalla si el parámetro de entrada keyCode es igual a KeyEvent.KEYCODE_ENTER. InputMethodManager controla si se muestra, se oculta un teclado de software y permite que el usuario elija qué teclado de software se muestra. El método muestra el valor "true" si el evento clave se manejó y muestra el valor "false" en caso contrario.

MainActivity.kt

private fun handleKeyEvent(view: View, keyCode: Int): Boolean {
   if (keyCode == KeyEvent.KEYCODE_ENTER) {
       // Hide the keyboard
       val inputMethodManager =
           getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
       inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
       return true
   }
   return false
}
  1. Ahora adjunta un objeto de escucha de claves en el widget TextInputEditText. Recuerda que puedes acceder al widget de TextInputEditText mediante el objeto de vinculación como binding.costOfServiceEditText.

Llama al método setOnKeyListener() en el costOfServiceEditText y pasa un OnKeyListener. Esto es similar a cómo configuras un objeto de escucha de clics en el botón calcular en la app con binding.calculateButton.setOnClickListener { calculateTip() }..

El código para configurar un objeto de escucha de claves en una vista es un poco más complejo, pero la idea general es que OnKeyListener tiene un método onKey() que se activa cuando se presiona una tecla. El método onKey() toma 3 argumentos de entrada: la vista, el código de la clave que se presionó y un evento clave (que no usarás, de modo que puedes llamar a "_"). Cuando se llama al método onKey(), debes llamar a tu método handleKeyEvent() y pasar los objetos de vista de código y vista. La sintaxis para escribir esto es: view, keyCode, _ -> handleKeyEvent(view, keyCode). Esto se denomina una expresión lambda, pero obtendrás más información sobre lambdas en una unidad posterior.

Agrega el código para configurar el objeto de escucha de claves en el campo de texto dentro del método onCreate() de la actividad. Esto es porque deseas que el objeto de escucha de claves se adjunte tan pronto como se cree el diseño y antes de que el usuario comience a interactuar con la actividad.

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
   ...

   setContentView(binding.root)

   binding.calculateButton.setOnClickListener { calculateTip() }

   binding.costOfServiceEditText.setOnKeyListener { view, keyCode, _ -> handleKeyEvent(view, keyCode)
   }
}
  1. Prueba que tus cambios nuevos funcionen. Ejecuta la app e ingresa un costo de servicio. Presiona la tecla Intro en el teclado y el teclado en pantalla debería ocultarse.

Cómo probar tu app con TalkBack habilitado

Como estuviste aprendiendo a lo largo de este curso, deseas compilar apps que sean accesibles para tantos usuarios como sea posible. Algunos usuarios pueden usar Talkback para acceder a tu app y navegar por ella. TalkBack es el lector de pantalla de Google incluido en los dispositivos Android. Proporciona comentarios por voz que permiten usar un dispositivo sin mirar la pantalla.

Con TalkBack habilitado, asegúrate de que un usuario pueda completar el caso de uso de cómo calcular la propina en tu app.

  1. Para habilitar TalkBack en tu dispositivo, sigue estas instrucciones.
  2. Regresa a la app de Tip Time.
  3. Explora tu app con Talkback usando estas instrucciones. Desliza el dedo hacia la derecha para navegar por los elementos de la pantalla en secuencia y desliza el dedo hacia la izquierda para ir en la dirección opuesta. Presiona dos veces en cualquier lugar para realizar una selección. Verifica que puedas llegar a todos los elementos de la app con gestos de deslizamiento.
  4. Asegúrate de que un usuario de TalkBack pueda navegar a cada elemento de la pantalla, ingresar un costo de servicio, cambiar las opciones de propina, calcular la propina y escuchar la propina. Recuerda que no se proporcionan comentarios por voz para los íconos, ya que los marcaste como importantForAccessibility="no".

Para obtener más información sobre cómo mejorar la accesibilidad de tu app, consulta estos principios.

(Opcional) Cómo ajustar el tono de los elementos de diseño vectoriales

En esta tarea opcional, aplicarás tonos para los íconos según el color principal del tema, de modo que los íconos se vean de manera diferente en el tema claro u oscuro (como se muestra a continuación). Este cambio es una buena adición a tu IU para que los íconos parezcan más coherentes con el tema de la app.

77092f702beb1cfb.png 80a390087905eb29.png

Como mencionamos antes, una de las ventajas de VectorDrawables frente a las imágenes de mapa de bits es la capacidad de ajustarlas y ajustarles el tono. A continuación, se detalla el XML que representa el ícono de campana. Hay dos atributos de color específicos para tener en cuenta: android:tint y android:fillColor.

ic_service.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
   android:width="24dp"
   android:height="24dp"
   android:viewportWidth="24"
   android:viewportHeight="24"
   android:tint="?attr/colorControlNormal">
 <path
     android:fillColor="@android:color/white"
     android:pathData="M2,17h20v2L2,19zM13.84,7.79c0.1,-0.24 0.16,-0.51 0.16,-0.79 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2c0,0.28 0.06,0.55 0.16,0.79C6.25,8.6 3.27,11.93 3,16h18c-0.27,-4.07 -3.25,-7.4 -7.16,-8.21z"/>
</vector>

bdddc76d0ca06573.png

Si hay un tono presente, se anulará cualquier directiva de fillColor para el elemento de diseño. En este caso, el color blanco se anula con el atributo del tema colorControlNormal. colorControlNormal es el color de "normal" (estado no seleccionado/desactivado) de un widget. Esto es un color gris.

Una mejora visual que podemos realizar en la app es cambiar el tono del elemento de diseño según el color principal del tema de la app. En el tema claro, el ícono aparecerá como @color/green, mientras que en el Tema oscuro, el ícono aparecerá como @color/green_light, que es el ?attr/colorPrimary. El ajuste de tono del elemento de diseño según el color principal del tema de la app puede hacer que los elementos del diseño se vean más unificados y coherentes. Esto también nos evita tener que duplicar el conjunto de íconos para el tema claro y el Tema oscuro. Solo hay un conjunto de elementos de diseño vectoriales, y el tono cambiará en el atributo del tema colorPrimary.

  1. Cambia el valor del atributo android:tint en ic_service.xml
android:tint="?attr/colorPrimary"

En Android Studio, el ícono ahora tiene el tono adecuado.

148a05c44b515c25.png

El valor al que apunta el atributo colorPrimary del tema variará según el tema claro o el oscuro.

  1. Repite lo mismo para cambiar el tono en los otros elementos de diseño vectoriales.

ic_store.xml

<vector ...
   android:tint="?attr/colorPrimary">
   ...
</vector>

ic_round_up.xml

<vector ...
   android:tint="?attr/colorPrimary">
   ...
</vector>
  1. Ejecuta la app. Verifica que los íconos se vean de manera diferente en los temas claros y oscuros.
  2. Como último paso de limpieza, recuerda cambiar el formato de todos los archivos de código XML y Kotlin en tu app.

Felicitaciones. Completaste la app de la calculadora de propinas. Felicitaciones por lo que compilaste. Esperamos que este sea un punto de partida para que desarrolles apps aún más hermosas y funcionales.

El código de solución para este codelab se encuentra en el repositorio de GitHub que se indica a continuación.

5743ac5ee2493d7.png ab4acfeed8390465.png

A fin de 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. En esa página, haz clic en el botón Code, que abre un cuadro de diálogo.

Eme2bJP46u-pMpnXVfm-bS2N2dlyq6c0jn1DtQYqBaml7TUhzXDWpYoDI0lGKi4xndE_uJw8sKfwfOZ1fC503xCVZrbh10JKJ4iEHdLDwFfdvnOheNxkokITW1LW6UZTncVJJUZ5Fw

  1. En el cuadro de diálogo, 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 an existing Android Studio project.

Tdjf5eS2nCikM9KdHgFaZNSbIUCzKXP6WfEaKVE2Oz1XIGZhgTJYlaNtXTHPFU1xC9pPiaD-XOPdIxVxwZAK8onA7eJyCXz2Km24B_8rpEVI_Po5qlcMNN8s4Tkt6kHEXdLQTDW7mg

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

PaMkVnfCxQqSNB1LxPpC6C6cuVCAc8jWNZCqy5tDVA6IO3NE2fqrfJ6p6ggGpk7jd27ybXaWU7rGNOFi6CvtMyHtWdhNzdAHmndzvEdwshF_SG24Le01z7925JsFa47qa-Q19t3RxQ

  1. En el cuadro de diálogo Import Project, navega hasta donde se encuentra la carpeta de proyecto descomprimido (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 j7ptomO2PEQNe8jFt4nKCOw_Oc_Aucgf4l_La8fGLCMLy0t9RN9SkmBFGOFjkEzlX4ce2w2NWq4J30sDaxEe4MaSNuJPpMgHxnsRYoBtIV3-GUpYYcIvRJ2HrqR27XGuTS4F7lKCzg para compilar y ejecutar la app. Asegúrate de que funcione como se espera.
  5. Explora los archivos del proyecto en la ventana de herramientas Project para ver cómo se implementó la app.
  • Usa componentes de Material Design cuando sea posible para cumplir con los lineamientos de Material Design y permitir una mayor personalización.
  • Agrega íconos para dar a los usuarios indicaciones visuales sobre el funcionamiento de las partes de tu app.
  • Usa ConstraintLayout para posicionar elementos en tu diseño.
  • Prueba tu app para casos extremos (p. ej., gira tu app en modo de paisaje) y realiza mejoras cuando corresponda.
  • Comenta tu código de modo que ayudes a que las personas que lo lean puedan entender tu enfoque.
  • Cambia el formato del código y límpialo para que sea lo más conciso posible.
  • Como continuación de los codelabs anteriores, actualiza tu app de conversor de unidades de cocina a fin de cumplir más de cerca los lineamientos de Material. Para ello, usa las prácticas recomendadas que aprendiste aquí (como usar los componentes de Material Design).