MDC-102 Android: Estructura de Material y diseño (Kotlin)

logo_components_color_2x_web_96dp.png

Los componentes de Material (MDC) ayudan a los desarrolladores a implementar Material Design. Los MDC, creados por un equipo de ingenieros y diseñadores de UX en Google, cuentan con decenas de componentes de IU atractivos y funcionales, y están disponibles para Android, iOS, la Web y Flutter.

material.io/develop

En el codelab MDC-101, usaste dos componentes de Material (MDC) para crear una página de acceso: campos de texto y botones con efectos de tinta. Ahora vamos a agregar navegación, estructura y datos para expandir esta base.

Qué compilarás

En este codelab, compilarás una pantalla principal para una app llamada Shrine, una aplicación de comercio electrónico en la que se vende ropa y artículos para el hogar. Contiene lo siguiente:

  • Una barra superior de la app
  • Una lista de cuadrícula llena de productos

Componentes de MDC-Android en este codelab

  • AppBarLayout
  • MaterialCardView

Requisitos

  • Conocimientos básicos de desarrollo de Android
  • Android Studio (descárgalo aquí si todavía no lo tienes)
  • Un emulador o dispositivo Android (disponible a través de Android Studio)
  • El código de muestra (consulta el siguiente paso)

¿Cómo calificarías tu nivel de experiencia con la compilación de apps para Android?

Principiante Intermedio Avanzado

¿Vienes de MDC-101?

Si completaste MDC-101, tu código debería estar preparado para este codelab. Ve al paso 3: Cómo agregar una barra superior de la app.

¿Empiezas de cero?

Descarga la app de inicio del codelab

Descargar app de inicio

La app de inicio se encuentra en el directorio material-components-android-codelabs-102-starter/kotlin. Asegúrate de usar cd en ese directorio antes de comenzar.

… o clónalo desde GitHub

Para clonar este codelab desde GitHub, ejecuta los siguientes comandos:

git clone https://github.com/material-components/material-components-android-codelabs
cd material-components-android-codelabs/
git checkout 102-starter

Cómo cargar el código de inicio en Android Studio

  1. Una vez que finalice el asistente de configuración y aparezca la ventana Welcome to Android Studio, haz clic en Open an existing Android Studio project. Navega al directorio en el que instalaste el código de muestra y selecciona kotlin -> shrine (o busca shrine en tu computadora) para abrir el proyecto de envío.
  2. Espera un momento para que Android Studio compile y sincronice el proyecto, como se muestra en los indicadores de actividad de la parte inferior de la ventana de Android Studio.
  3. En este punto, es posible que Android Studio genere algunos errores de compilación, ya que te faltan el SDK de Android o las herramientas de compilación, como se muestra más abajo. Sigue las instrucciones de Android Studio para instalar o actualizar estos elementos y sincronizar tu proyecto.

Agrega dependencias del proyecto

El proyecto necesita una dependencia en la biblioteca de compatibilidad con MDC Android. El código de muestra que descargaste ya debería tener esta dependencia, pero es una buena práctica seguir los siguientes pasos para asegurarte.

  1. Navega hasta el archivo build.gradle del módulo app y asegúrate de que el bloque dependencies incluya una dependencia en MDC Android:
api 'com.google.android.material:material:1.1.0-alpha06'
  1. (Opcional) Si es necesario, edita el archivo build.gradle para agregar las siguientes dependencias y sincronizar el proyecto.
dependencies {
    api 'com.google.android.material:material:1.1.0-alpha06'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'com.android.volley:volley:1.1.1'
    implementation 'com.google.code.gson:gson:2.8.5'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:core:1.1.0'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test:runner:1.2.0-alpha05'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha05'
}

Cómo ejecutar la app de inicio

  1. Asegúrate de que la configuración de compilación ubicada a la izquierda del botón Ejecutar / Reproducir sea app.
  2. Presiona el botón verde Run/Play para compilar y ejecutar la app.
  3. En la ventana Select Deployment Target, si ya aparece un dispositivo Android en los dispositivos disponibles, ve al paso 8. De lo contrario, haz clic en Create New Virtual Device.
  4. En la pantalla Select Hardware, selecciona un teléfono, por ejemplo, Pixel 2, y haz clic en Next.
  5. En la pantalla System Image, selecciona una versión reciente de Android, preferentemente, el nivel de API más alto. Si no está instalada, haz clic en el vínculo Download que aparece y completa la descarga.
  6. Haz clic en Next.
  7. En la pantalla Android Virtual Device (AVD), deja los ajustes tal como están y haz clic en Finish.
  8. Selecciona un dispositivo Android en el diálogo de destino de implementación.
  9. Haz clic en Aceptar.
  10. Android Studio compila la app, la implementa y la abre automáticamente en el dispositivo de destino.

¡Listo! Deberías ver la página de acceso de Shrine del codelab MDC-101.

Ahora que la pantalla de acceso se ve bien, vamos a propagar la app con algunos productos.

La pantalla principal se revela cuando se descarta la página de acceso, con una pantalla que dice "¡Lo lograste!". Fantástico. Pero ahora nuestro usuario no tiene ninguna acción para realizar ni sabe en qué parte de la app está. Para solucionarlo, es momento de agregar la navegación.

Material Design ofrece patrones de navegación que garantizan un alto grado de usabilidad. Uno de los componentes más visibles es la barra superior de la app.

Para posibilitar la navegación y brindar a los usuarios acceso rápido a otras acciones, agregaremos una barra superior de la app.

Cómo agregar un widget de AppBar

En shr_product_grid_fragment.xml, borra el bloque <LinearLayout> que contiene el mensaje "¡Lo lograste!". TextView y reemplázalo por lo siguiente:

shr_product_grid_fragment.xml

<com.google.android.material.appbar.AppBarLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

   <androidx.appcompat.widget.Toolbar
       android:id="@+id/app_bar"
       style="@style/Widget.Shrine.Toolbar"
       android:layout_width="match_parent"
       android:layout_height="?attr/actionBarSize"
       app:title="@string/shr_app_name" />
</com.google.android.material.appbar.AppBarLayout>

Tu shr_product_grid_fragment.xml ahora debería verse de la siguiente manera:

shr_product_grid_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".ProductGridFragment">

   <com.google.android.material.appbar.AppBarLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content">

       <androidx.appcompat.widget.Toolbar
           android:id="@+id/app_bar"
           style="@style/Widget.Shrine.Toolbar"
           android:layout_width="match_parent"
           android:layout_height="?attr/actionBarSize"
           app:title="@string/shr_app_name" />
   </com.google.android.material.appbar.AppBarLayout>
  
</FrameLayout>

Muchas barras de la app tienen un botón junto al título. Agreguemos un ícono de menú en el nuestro.

Cómo agregar un ícono de navegación

Mientras aún estás en shr_product_grid_fragment.xml, agrega lo siguiente al componente XML Toolbar que acabas de agregar a tu diseño:

shr_product_grid_fragment.xml

app:navigationIcon="@drawable/shr_menu"

Tu shr_product_grid_fragment.xml debería verse de la siguiente manera:

shr_product_grid_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".ProductGridFragment">
  
   <com.google.android.material.appbar.AppBarLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content">

       <androidx.appcompat.widget.Toolbar
           android:id="@+id/app_bar"
           style="@style/Widget.Shrine.Toolbar"
           android:layout_width="match_parent"
           android:layout_height="?attr/actionBarSize"
           app:navigationIcon="@drawable/shr_menu"
           app:title="@string/shr_app_name" />
   </com.google.android.material.appbar.AppBarLayout>
  
</FrameLayout>

Cómo agregar botones de acción y aplicar estilo a la barra superior de la aplicación

También puedes agregar botones al extremo final de la barra de la app. En Android, se denominan botones de acción. Diseñaremos la barra superior de la app y agregaremos botones de acción a su menú de forma programática.

En la función onCreateView de ProductGridFragment.kt, configura el Toolbar de activity para que se use como un ActionBar con setSupportActionBar. Puedes hacerlo después de crear la vista con inflater.

ProductGridFragment.kt

override fun onCreateView(
       inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
   // Inflate the layout for this fragment with the ProductGrid theme
   val view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false)

   // Set up the toolbar.
   (activity as AppCompatActivity).setSupportActionBar(view.app_bar)

   return view;
}

A continuación, justo debajo del método que acabamos de cambiar para configurar la barra de herramientas, anularemos onCreateOptionsMenu para inflar el contenido de shr_toolbar_menu.xml en la barra de herramientas:

ProductGridFragment.kt

override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
   menuInflater.inflate(R.menu.shr_toolbar_menu, menu)
   super.onCreateOptionsMenu(menu, menuInflater)
}

Por último, anula onCreate() en ProductGridFragment.kt y, después de llamar a super(), llama a setHasOptionMenu con true:

ProductGridFragment.kt

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setHasOptionsMenu(true)
}

Los fragmentos de código anteriores configuran la barra de la app de nuestro diseño XML para que sea la barra de acciones de esta actividad. La devolución de llamada onCreateOptionsMenu le indica a la actividad qué usar como nuestro menú. En este caso, colocará los elementos del menú de R.menu.shr_toolbar_menu en la barra de la app. El archivo de menú contiene dos elementos: "Search" y "Filter".

shr_toolbar_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
   <item
       android:id="@+id/search"
       android:icon="@drawable/shr_search"
       android:title="@string/shr_search_title"
       app:showAsAction="always" />
   <item
       android:id="@+id/filter"
       android:icon="@drawable/shr_filter"
       android:title="@string/shr_filter_title"
       app:showAsAction="always" />
</menu>

Después de esos cambios, tu archivo ProductGridFragment.kt debería verse de la siguiente manera:

ProductGridFragment.kt

package com.google.codelabs.mdc.kotlin.shrine

import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import com.google.codelabs.mdc.kotlin.shrine.network.ProductEntry
import kotlinx.android.synthetic.main.shr_product_grid_fragment.view.*

class ProductGridFragment : Fragment() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setHasOptionsMenu(true)
   }

   override fun onCreateView(
           inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
       // Inflate the layout for this fragment with the ProductGrid theme
       val view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false)

       // Set up the tool bar
       (activity as AppCompatActivity).setSupportActionBar(view.app_bar)

       return view;
   }

   override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
       menuInflater.inflate(R.menu.shr_toolbar_menu, menu)
       super.onCreateOptionsMenu(menu, menuInflater)
   }
}

Realiza la compilación y ejecuta la aplicación. La pantalla principal debería verse de la siguiente manera:

Ahora la barra de herramientas tiene un ícono de navegación, un título y dos íconos de acción en el lado derecho. La barra de herramientas también muestra la elevación con una sombra sutil que indica que se encuentra en una capa diferente a la del contenido.

Ahora que nuestra app tiene cierta estructura, coloquemos el contenido en tarjetas para organizarlo.

Agregar una tarjeta

Comencemos por agregar una tarjeta debajo de la barra superior de la app. Una tarjeta debe tener una región para una imagen, un título y una etiqueta para el texto secundario. Agrega lo siguiente en shr_product_grid_fragment.xml debajo de AppBarLayout.

shr_product_grid_fragment.xml

<com.google.android.material.card.MaterialCardView
   android:layout_width="160dp"
   android:layout_height="180dp"
   android:layout_marginBottom="16dp"
   android:layout_marginLeft="16dp"
   android:layout_marginRight="16dp"
   android:layout_marginTop="70dp"
   app:cardBackgroundColor="?attr/colorPrimaryDark"
   app:cardCornerRadius="4dp">

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_gravity="bottom"
       android:background="#FFFFFF"
       android:orientation="vertical"
       android:padding="8dp">

       <TextView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:padding="2dp"
           android:text="@string/shr_product_title"
           android:textAppearance="?attr/textAppearanceHeadline6" />

       <TextView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:padding="2dp"
           android:text="@string/shr_product_description"
           android:textAppearance="?attr/textAppearanceBody2" />
   </LinearLayout>
</com.google.android.material.card.MaterialCardView>

Compila y ejecuta:

En esta vista previa, puedes ver que la tarjeta está insertada desde el borde izquierdo, tiene esquinas redondeadas y una sombra (que expresa la elevación de la tarjeta). Todo el elemento se denomina "contenedor". A excepción del contenedor, todos los elementos que contiene son opcionales.

Puedes agregar los siguientes elementos a un contenedor: texto de encabezado, una miniatura o un avatar, texto de subtítulo, divisores y hasta íconos y botones. Por ejemplo, la tarjeta que acabamos de crear contiene dos TextView (uno para el título y otro para el texto secundario) en un LinearLayout, alineados en la parte inferior de la tarjeta.

Las tarjetas suelen mostrarse en una colección con otras tarjetas. En la próxima sección de este codelab, los distribuiremos como una colección en una cuadrícula.

Cuando hay varias tarjetas presentes en una pantalla, se agrupan en una o más colecciones. Las tarjetas en una cuadrícula son coplanarias, lo que significa que comparten la misma elevación en reposo que las otras (a menos que se recojan o se arrastren, pero no lo abordaremos en este codelab).

Configura la cuadrícula de tarjetas

Consulta el archivo shr_product_card.xml que te proporcionamos:

shr_product_card.xml

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   app:cardBackgroundColor="@android:color/white"
   app:cardElevation="2dp"
   app:cardPreventCornerOverlap="true">

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="vertical">

       <com.android.volley.toolbox.NetworkImageView
           android:id="@+id/product_image"
           android:layout_width="match_parent"
           android:layout_height="@dimen/shr_product_card_image_height"
           android:background="?attr/colorPrimaryDark"
           android:scaleType="centerCrop" />

       <LinearLayout
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:orientation="vertical"
           android:padding="16dp">

           <TextView
               android:id="@+id/product_title"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:text="@string/shr_product_title"
               android:textAppearance="?attr/textAppearanceHeadline6" />

           <TextView
               android:id="@+id/product_price"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:text="@string/shr_product_description"
               android:textAppearance="?attr/textAppearanceBody2" />
       </LinearLayout>
   </LinearLayout>
</com.google.android.material.card.MaterialCardView>

Este diseño de tarjeta contiene una tarjeta con una imagen (en este caso, un NetworkImageView, que nos permite cargar y mostrar imágenes desde una URL) y dos TextViews.

A continuación, observa el ProductCardRecyclerViewAdapter que te proporcionamos. Se encuentra en el mismo paquete que ProductGridFragment.

ProductCardRecyclerViewAdapter.kt

package com.google.codelabs.mdc.kotlin.shrine

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView

import com.google.codelabs.mdc.kotlin.shrine.network.ProductEntry

/**
* Adapter used to show a simple grid of products.
*/
class ProductCardRecyclerViewAdapter(private val productList: List<ProductEntry>) : RecyclerView.Adapter<ProductCardViewHolder>() {

   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductCardViewHolder {
       val layoutView = LayoutInflater.from(parent.context).inflate(R.layout.shr_product_card, parent, false)
       return ProductCardViewHolder(layoutView)
   }

   override fun onBindViewHolder(holder: ProductCardViewHolder, position: Int) {
       // TODO: Put ViewHolder binding code here in MDC-102
   }

   override fun getItemCount(): Int {
       return productList.size
   }
}

La clase del adaptador anterior administra el contenido de nuestra cuadrícula. Para determinar qué debe hacer cada vista con el contenido proporcionado, pronto escribiremos el código de onBindViewHolder().

En el mismo paquete, también puedes consultar ProductCardViewHolder. Esta clase almacena las vistas que afectan el diseño de la tarjeta, de modo que podamos modificarlas más adelante.

ProductCardViewHolder.kt

package com.google.codelabs.mdc.kotlin.shrine

import android.view.View
import androidx.recyclerview.widget.RecyclerView

class ProductCardViewHolder(itemView: View) //TODO: Find and store views from itemView
   : RecyclerView.ViewHolder(itemView)

Para configurar nuestra cuadrícula, primero quitaremos el marcador de posición MaterialCardView de shr_product_grid_fragment.xml. A continuación, debes agregar el componente que representa nuestra cuadrícula de tarjetas. En este caso, usaremos un RecyclerView. Agrega el componente RecyclerView a tu shr_product_grid_fragment.xml debajo del componente AppBarLayout XML:

shr_product_grid_fragment.xml

<androidx.core.widget.NestedScrollView
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:layout_marginTop="56dp"
   android:background="@color/productGridBackgroundColor"
   android:paddingStart="@dimen/shr_product_grid_spacing"
   android:paddingEnd="@dimen/shr_product_grid_spacing"
   app:layout_behavior="@string/appbar_scrolling_view_behavior">

   <androidx.recyclerview.widget.RecyclerView
       android:id="@+id/recycler_view"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />

</androidx.core.widget.NestedScrollView>

Tu shr_product_grid_fragment.xml debería verse de la siguiente manera:

shr_product_grid_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".ProductGridFragment">

   <com.google.android.material.appbar.AppBarLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content">

       <androidx.appcompat.widget.Toolbar
           android:id="@+id/app_bar"
           style="@style/Widget.Shrine.Toolbar"
           android:layout_width="match_parent"
           android:layout_height="?attr/actionBarSize"
           app:navigationIcon="@drawable/shr_menu"
           app:title="@string/shr_app_name" />
   </com.google.android.material.appbar.AppBarLayout>

   <androidx.core.widget.NestedScrollView
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_marginTop="56dp"
       android:background="@color/productGridBackgroundColor"
       android:paddingStart="@dimen/shr_product_grid_spacing"
       android:paddingEnd="@dimen/shr_product_grid_spacing"
       app:layout_behavior="@string/appbar_scrolling_view_behavior">

       <androidx.recyclerview.widget.RecyclerView
           android:id="@+id/recycler_view"
           android:layout_width="match_parent"
           android:layout_height="match_parent" />

   </androidx.core.widget.NestedScrollView>

</FrameLayout>

Por último, en onCreateView(), agrega el código de inicialización de RecyclerView en ProductGridFragment.kt después de llamar a setUpToolbar(view) y antes de la sentencia return:

ProductGridFragment.kt

override fun onCreateView(
       inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
   // Inflate the layout for this fragment with the ProductGrid theme
   val view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false)

   // Set up the toolbar.
   (activity as AppCompatActivity).setSupportActionBar(view.app_bar)

   // Set up the RecyclerView
   view.recycler_view.setHasFixedSize(true)
   view.recycler_view.layoutManager = GridLayoutManager(context, 2, RecyclerView.VERTICAL, false)
   val adapter = ProductCardRecyclerViewAdapter(
           ProductEntry.initProductEntryList(resources))
   view.recycler_view.adapter = adapter
   val largePadding = resources.getDimensionPixelSize(R.dimen.shr_product_grid_spacing)
   val smallPadding = resources.getDimensionPixelSize(R.dimen.shr_product_grid_spacing_small)
   view.recycler_view.addItemDecoration(ProductGridItemDecoration(largePadding, smallPadding))

   return view;
}

El fragmento de código anterior contiene los pasos de inicialización necesarios para configurar un RecyclerView. Esto incluye configurar el administrador de diseño de RecyclerView, además de inicializar y configurar el adaptador de RecyclerView.

Tu archivo ProductGridFragment.kt ahora debería verse de la siguiente manera:

ProductGridFragment.kt

package com.google.codelabs.mdc.kotlin.shrine

import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.codelabs.mdc.kotlin.shrine.network.ProductEntry
import kotlinx.android.synthetic.main.shr_product_grid_fragment.view.*

class ProductGridFragment : Fragment() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setHasOptionsMenu(true)
   }

   override fun onCreateView(
           inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
       // Inflate the layout for this fragment with the ProductGrid theme
       val view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false)

       // Set up the toolbar.
       (activity as AppCompatActivity).setSupportActionBar(view.app_bar)

       // Set up the RecyclerView
       view.recycler_view.setHasFixedSize(true)
       view.recycler_view.layoutManager = GridLayoutManager(context, 2, RecyclerView.VERTICAL, false)
       val adapter = ProductCardRecyclerViewAdapter(
               ProductEntry.initProductEntryList(resources))
       view.recycler_view.adapter = adapter
       val largePadding = resources.getDimensionPixelSize(R.dimen.shr_product_grid_spacing)
       val smallPadding = resources.getDimensionPixelSize(R.dimen.shr_product_grid_spacing_small)
       view.recycler_view.addItemDecoration(ProductGridItemDecoration(largePadding, smallPadding))

       return view;
   }

   override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
       menuInflater.inflate(R.menu.shr_toolbar_menu, menu)
       super.onCreateOptionsMenu(menu, menuInflater)
   }
}

Compila y ejecuta:

Ahora sí aparecen las tarjetas. Aún no muestran nada, así que agreguemos algunos datos de productos.

Agrega imágenes y texto

Para cada tarjeta, agrega una imagen, el nombre del producto y el precio. Nuestra abstracción ViewHolder contiene las vistas de cada tarjeta. En nuestro ViewHolder, agrega las tres vistas de la siguiente manera.

ProductCardViewHolder.kt

package com.google.codelabs.mdc.kotlin.shrine

import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

import com.android.volley.toolbox.NetworkImageView

class ProductCardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

   var productImage: NetworkImageView = itemView.findViewById(R.id.product_image)
   var productTitle: TextView = itemView.findViewById(R.id.product_title)
   var productPrice: TextView = itemView.findViewById(R.id.product_price)
}

Actualiza el método onBindViewHolder() en ProductCardRecyclerViewAdapter para establecer el título, el precio y la imagen del producto para cada vista del producto, como se muestra a continuación:

ProductCardRecyclerViewAdapter.kt

override fun onBindViewHolder(holder: ProductCardViewHolder, position: Int) {
   if (position < productList.size) {
       val product = productList[position]
       holder.productTitle.text = product.title
       holder.productPrice.text = product.price
       ImageRequester.setImageFromUrl(holder.productImage, product.url)
   }
}

El código anterior le indica al adaptador de nuestro RecyclerView qué hacer con cada tarjeta, mediante un ViewHolder.

Aquí, se establecen los datos de texto en cada uno de los TextView de ViewHolder y se llama a un ImageRequester para obtener una imagen de una URL. ImageRequester es una clase que proporcionamos para tu comodidad y que usa la biblioteca Volley (este es un tema que está fuera del alcance de este codelab, pero puedes explorar el código por tu cuenta).

Compila y ejecuta:

Nuestros productos ya se muestran en la app.

Nuestra app tiene un flujo básico que lleva al usuario de la pantalla de acceso a una pantalla principal en la que se pueden ver los productos. Con unas pocas líneas de código, agregamos una barra superior de la app con un título y tres botones, y una cuadrícula de tarjetas para presentar el contenido de la app. Ahora, la pantalla principal es simple y funcional, y tiene una estructura básica y contenido de acción.

Próximos pasos

Con la barra superior de la app, las tarjetas, los campos de texto y los botones, usamos cuatro componentes principales de Material Design de la biblioteca de MDC-Android. Puedes visitar el catálogo de MDC-Android para explorar aún más componentes.

Aunque funciona en su totalidad, nuestra app aún no expresa ninguna marca ni estilo en particular. En MDC-103: Temas de Material Design con color, forma, elevación y tipo, personalizaremos el estilo de estos componentes para expresar una marca moderna y llamativa.

Pude completar este codelab con una cantidad de tiempo y esfuerzo razonables.

Totalmente de acuerdo De acuerdo Neutral En desacuerdo Totalmente en desacuerdo

Me gustaría seguir usando los componentes de Material en el futuro.

Totalmente de acuerdo De acuerdo Neutral En desacuerdo Totalmente en desacuerdo