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

1. Introducción

logo_components_color_2x_web_96dp.png

Los componentes de Material (MDC) ayudan a los desarrolladores a implementar Material Design. MDC, creado por un equipo de ingenieros y diseñadores de UX en Google, cuenta con decenas de componentes de IU atractivos y funcionales, y está disponible 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. 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 cuadriculada de productos

249db074eff043f4.png

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

2. Configura tu entorno de desarrollo

¿Vienes de MDC-101?

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

¿Empiezas de cero?

Descarga la app de inicio del codelab

La app de inicio se encuentra en el directorio material-components-android-codelabs-102-starter/java. Asegúrate de 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 java -> shrine (o busca shrine en tu computadora) para abrir el proyecto de Shrine.
  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.

F5H6srsw_5xOPGFpKrm1RwgewatxA_HUbDI1PWoQUAoJcT6DpfBOkAYwq3S-2vUHvweUaFgAmG7BtUKkGouUbhTwXQh53qec8tO5eVecdlo7QIoLc8rNxFEBb8l7RlS-KzBbZOzVhA

Agrega dependencias del proyecto

El proyecto necesita una dependencia en la biblioteca de compatibilidad de MDC para Android. El código de muestra que descargaste ya debería tener esta dependencia, pero es recomendable que sigas los pasos que se indican a continuación para asegurarte.

  1. Navega al archivo build.gradle del módulo app y asegúrate de que el bloque dependencies incluya una dependencia en MDC para 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 Run / Play 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 tienes un dispositivo Android en la lista de 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, como 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 la 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.

4cb0c218948144b4.png

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

3. Cómo agregar una barra superior de la app

La pantalla principal se muestra cuando descartas 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, vamos a agregar la navegación.

Material Design ofrece patrones de navegación que garantizan un alto grado de usabilidad. Uno de los componentes de navegación más notables 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 la etiqueta <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 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ú al nuestro.

Cómo agregar un ícono de navegación

Mientras 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 diseño a la barra superior de la aplicación

También puedes agregar botones en el 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.

Primero, crearemos un método para configurar la barra de herramientas. El método debería obtener una referencia a la barra de herramientas mediante su id y también una referencia a la actividad mediante getActivity(). Si la actividad no es nula, establece el Toolbar que se usará como ActionBar con setSupportActionBar:

ProductGridFragment.java

private void setUpToolbar(View view) {
   Toolbar toolbar = view.findViewById(R.id.app_bar);
   AppCompatActivity activity = (AppCompatActivity) getActivity();
   if (activity != null) {
       activity.setSupportActionBar(toolbar);
   }
}

A continuación, directamente debajo del método setUpToolbar que acabamos de agregar, anulemos onCreateOptionsMenu para aumentar el contenido de shr_toolbar_menu.xml en la barra de herramientas:

ProductGridFragment.java

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
   menuInflater.inflate(R.menu.shr_toolbar_menu, menu);
   super.onCreateOptionsMenu(menu, menuInflater);
}

Ahora, agrega una llamada al método setUpToolbar que agregamos al contenido del método onCreateView() con lo siguiente:

ProductGridFragment.java

@Override
public View onCreateView(
       @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
   // Inflate the layout for this fragment with the ProductGrid theme
   View view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false);

   // Set up the toolbar
   setUpToolbar(view);

   return view;
}

Por último, agrega un método onCreate() a ProductGridFragment.java. En el cuerpo del método, establece el parámetro de setHasOptionMenu como true.

El método debería verse de la siguiente manera:

ProductGridFragment.java

@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setHasOptionsMenu(true);
}

El código anterior establece que la barra de la app de nuestro diseño XML sea la barra de acción para esta actividad. La devolución de llamada onCreateOptionsMenu le indica a la actividad qué usar como menú. En este caso, se colocarán los elementos de menú de R.menu.shr_toolbar_menu en la barra de la app.

El archivo de menú contiene dos elementos: "Buscar" y "Filtrar".

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.java debería verse de la siguiente manera:

ProductGridFragment.java

package com.google.codelabs.mdc.java.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 android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;


public class ProductGridFragment extends Fragment {

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setHasOptionsMenu(true);
   }
  
   @Override
   public View onCreateView(
           @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
       // Inflate the layout for this fragment with the ProductGrid theme
       View view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false);

       // Set up the toolbar
       setUpToolbar(view);

       return view;
   }
  
   private void setUpToolbar(View view) {
       Toolbar toolbar = view.findViewById(R.id.app_bar);
       AppCompatActivity activity = (AppCompatActivity) getActivity();
       if (activity != null) {
           activity.setSupportActionBar(toolbar);
       }
   }

   @Override
   public void 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:

d04e8aa3b27f4754.png

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.

4. Agregar una tarjeta

Ahora que nuestra app tiene cierta estructura, vamos a colocar el contenido en tarjetas para organizarlo.

Cómo 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.

En shr_product_grid_fragment.xml, agrega lo siguiente 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>

Compilación y ejecución:

f6184a55ccb5f920.png

En esta vista previa, puedes ver que la tarjeta está insertada desde el borde izquierdo de la pantalla y tiene esquinas redondeadas y una sombra (que expresa la elevación de la tarjeta). Todo el área 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 avatar, texto de subtítulo, divisores e incluso íconos y botones. La tarjeta que acabamos de crear, por ejemplo, contiene dos TextView (uno para el título y otro para el texto secundario) en una LinearLayout, alineados con la parte inferior de la tarjeta.

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

5. Crea una cuadrícula de tarjetas

Cuando hay varias tarjetas en una pantalla, se agrupan en una o más colecciones. Las tarjetas en una cuadrícula son coplanares, lo que significa que comparten la misma elevación en reposo que las demás (a menos que se recojan o arrastran, pero no abordaremos eso 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 (aquí un NetworkImageView, que nos permite incluir imágenes de una URL) y dos TextViews.

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

ProductCardRecyclerViewAdapter.java

package com.google.codelabs.mdc.java.shrine;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.google.codelabs.mdc.java.shrine.network.ImageRequester;
import com.google.codelabs.mdc.java.shrine.network.ProductEntry;

import java.util.List;

/**
* Adapter used to show a simple grid of products.
*/
public class ProductCardRecyclerViewAdapter extends RecyclerView.Adapter<ProductCardViewHolder> {

   private List<ProductEntry> productList;
   private ImageRequester imageRequester;

   ProductCardRecyclerViewAdapter(List<ProductEntry> productList) {
       this.productList = productList;
       imageRequester = ImageRequester.getInstance();
   }

   @NonNull
   @Override
   public ProductCardViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
       View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.shr_product_card, parent, false);
       return new ProductCardViewHolder(layoutView);
   }

   @Override
   public void onBindViewHolder(@NonNull ProductCardViewHolder holder, int position) {
       // TODO: Put ViewHolder binding code here in MDC-102
   }

   @Override
   public int getItemCount() {
       return productList.size();
   }
}

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

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

ProductCardViewHolder.java

package com.google.codelabs.mdc.java.shrine;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;

public class ProductCardViewHolder extends RecyclerView.ViewHolder {

   public ProductCardViewHolder(@NonNull View itemView) {
       super(itemView);
       // TODO: Find and store views from itemView
   }
}

Para configurar nuestra cuadrícula, primero debemos quitar 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, agrega un componente RecyclerView a tu shr_product_grid_fragment.xml debajo del componente XML AppBarLayout:

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 a ProductGridFragment.java después de llamar a setUpToolbar(view) y antes de la sentencia return:

ProductGridFragment.java

@Override
public View onCreateView(
       @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
   ...
   setUpToolbar(view);

   // Set up the RecyclerView
   RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
   recyclerView.setHasFixedSize(true);
   recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2, GridLayoutManager.VERTICAL, false));
   ProductCardRecyclerViewAdapter adapter = new ProductCardRecyclerViewAdapter(
           ProductEntry.initProductEntryList(getResources()));
   recyclerView.setAdapter(adapter);
   int largePadding = getResources().getDimensionPixelSize(R.dimen.shr_product_grid_spacing);
   int smallPadding = getResources().getDimensionPixelSize(R.dimen.shr_product_grid_spacing_small);
   recyclerView.addItemDecoration(new 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.

Ahora, tu archivo ProductGridFragment.java debería verse de la siguiente manera:

ProductGridFragment.java

package com.google.codelabs.mdc.java.shrine;

import android.os.Bundle;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;


import com.google.codelabs.mdc.java.shrine.network.ProductEntry;

public class ProductGridFragment extends Fragment {

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setHasOptionsMenu(true);
   }

   @Override
   public View onCreateView(
           @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
       // Inflate the layout for this fragment with the ProductGrid theme
       View view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false);

       // Set up the toolbar
       setUpToolbar(view);

       // Set up the RecyclerView
       RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
       recyclerView.setHasFixedSize(true);
       recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2, GridLayoutManager.VERTICAL, false));
       ProductCardRecyclerViewAdapter adapter = new ProductCardRecyclerViewAdapter(
               ProductEntry.initProductEntryList(getResources()));
       recyclerView.setAdapter(adapter);
       int largePadding = getResources().getDimensionPixelSize(R.dimen.shr_product_grid_spacing);
       int smallPadding = getResources().getDimensionPixelSize(R.dimen.shr_product_grid_spacing_small);
       recyclerView.addItemDecoration(new ProductGridItemDecoration(largePadding, smallPadding));

       return view;
   }

   private void setUpToolbar(View view) {
       Toolbar toolbar = view.findViewById(R.id.app_bar);
       AppCompatActivity activity = (AppCompatActivity) getActivity();
       if (activity != null) {
           activity.setSupportActionBar(toolbar);
       }
   }

   @Override
   public void 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.

f9aeab846fc3bb4c.png

¡Las cartas están allí ahora! 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.java

package com.google.codelabs.mdc.java.shrine;

import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;

import com.android.volley.toolbox.NetworkImageView;

public class ProductCardViewHolder extends RecyclerView.ViewHolder {

   public NetworkImageView productImage;
   public TextView productTitle;
   public TextView productPrice;

   public ProductCardViewHolder(@NonNull View itemView) {
       super(itemView);
       productImage = itemView.findViewById(R.id.product_image);
       productTitle = itemView.findViewById(R.id.product_title);
       productPrice = itemView.findViewById(R.id.product_price);
   }
}

En nuestro adaptador de RecyclerView, en ViewHolder,, actualiza el método onBindViewHolder() para configurar la información en cada vista:

ProductCardRecyclerViewAdapter.java

@Override
public void onBindViewHolder(@NonNull ProductCardViewHolder holder, int position) {
   if (productList != null && position < productList.size()) {
       ProductEntry product = productList.get(position);
       holder.productTitle.setText(product.title);
       holder.productPrice.setText(product.price);
       imageRequester.setImageFromUrl(holder.productImage, product.url);
   }
}

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

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

Compilación y ejecución:

249db074eff043f4.png

Nuestros productos ya se muestran en la app.

6. Resumen

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 solo 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 nuestra 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, el campo de texto y el botón, ahora usamos cuatro componentes principales de Material Design de la biblioteca de MDC-Android. Puedes explorar aún más componentes en el catálogo de MDC-Android componentes en MDC Android.

Aunque es completamente funcional, nuestra app aún no expresa ninguna marca 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.

Muy de acuerdo De acuerdo Neutral En desacuerdo Muy en desacuerdo