MDC-102 Android: estrutura e layout do Material Design (Kotlin)

1. Introdução

logo_components_color_2x_web_96dp.png

Os componentes do Material Design (MDC, na sigla em inglês) ajudam os desenvolvedores a implementar o Material Design. Criados por uma equipe de engenheiros e designers de UX do Google, os MDC apresentam dezenas de componentes de interface bonitos e funcionais e estão disponíveis para Android, iOS, Web e Flutter.material.io/develop

No codelab MDC-101, você usou dois componentes do Material Design (MDC) para criar uma página de login: campos de texto e botões com ondulações de tinta. Agora, vamos nos aprofundar nessa base adicionando navegação, estrutura e dados.

O que você criará

Neste codelab, você criará uma tela inicial para o app Shrine, um app de comércio eletrônico que vende roupas e artigos domésticos. Ele conterá:

  • Uma barra de apps superior.
  • Uma lista de grade cheia de produtos.

249db074eff043f4.png

Componentes MDC-Android neste codelab

  • AppBarLayout
  • MaterialCardView

O que é necessário

  • Conhecimento básico de desenvolvimento para Android.
  • Android Studio (faça o download dele aqui, caso ainda não tenha feito)
  • Um dispositivo ou emulador Android (disponível no Android Studio)
  • O exemplo de código (confira a próxima etapa)

Como você classificaria seu nível de experiência na criação de apps Android?

Iniciante Intermediário Proficiente

2. Configurar o ambiente de desenvolvimento

Está continuando do MDC-101?

Se você concluiu o MDC-101, o código para este codelab já está pronto. Pule para a etapa 3: Adicionar uma barra de apps na parte de cima.

Está começando do zero?

Faça o download do app inicial do codelab

O app inicial está localizado no diretório material-components-android-codelabs-102-starter/kotlin. Insira cd nesse diretório antes de começar.

... ou clone-o do GitHub

Para clonar este codelab do GitHub, execute estes comandos:

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

Carregar o código inicial no Android Studio

  1. Quando o assistente de configuração for concluído e a janela Welcome to Android Studio for exibida, clique em Open an existing Android Studio project. Navegue até o diretório em que você instalou o exemplo de código e selecione kotlin -> santuário (ou pesquise por shrine no computador) para abrir o projeto de envio.
  2. Aguarde um pouco enquanto o Android Studio cria e sincroniza o projeto, conforme mostrado nos indicadores de atividade na parte inferior da janela.
  3. Como o SDK do Android ou as ferramentas de compilação não estão presentes, o Android Studio poderá encontrar alguns erros de compilação. Veja um exemplo a seguir. Siga as instruções no Android Studio para instalar/atualizar essas ferramentas e sincronizar o projeto.

KzoYWC1S7Se7yL8igi1vXF_mbVxAdl2lg5kb7RODrsVpEng0G6U3NK1Qnn0faBBZd2u71yMXioy9tD-7fv3NXvVO4N3EtMMeWDTmqBMMl6egd9R5uXX0T_SKmahbmRor3wZZHX0ByA

Adicionar dependências do projeto

O projeto precisa de uma dependência na Biblioteca de Suporte do Android para MDC. O exemplo de código que você baixou já deve ter essa dependência listada, mas é recomendável seguir as etapas a seguir para ter certeza.

  1. Navegue até o arquivo build.gradle do módulo app e verifique se o bloco dependencies inclui uma dependência no MDC Android:
api 'com.google.android.material:material:1.1.0-alpha06'
  1. (Opcional) Se necessário, edite o arquivo build.gradle para adicionar as dependências a seguir e sincronizar o projeto.
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'
}

Executar o app inicial

  1. Confira se a configuração do build à esquerda do botão Run / Play é app.
  2. Pressione o botão verde Run / Play para criar e executar o app.
  3. Na janela Select Deployment Target, se você já tiver um dispositivo Android listado entre os dispositivos disponíveis, pule para a Etapa 8. Caso contrário, clique em Create New Virtual Device.
  4. Na tela Select Hardware, selecione um smartphone, como o Pixel 2, e clique em Next.
  5. Na tela System Image, selecione uma versão recente do Android, de preferência o nível da API mais alto. Se ele não estiver instalado, clique no link Download exibido e faça o download.
  6. Clique em Next.
  7. Na tela Android Virtual Device (AVD), deixe as configurações como estão e clique em Finish.
  8. Selecione um dispositivo Android na caixa de diálogo de destino da implantação.
  9. Clique em OK.
  10. O Android Studio cria o app, faz a implantação e o abre automaticamente no dispositivo de destino.

Pronto. A página de login do Shrine do codelab MDC-101 será exibida.

4cb0c218948144b4.png

Agora que a tela de login está pronta, vamos preencher o app com alguns produtos.

3. Adicionar uma barra de apps superior

A tela inicial é revelada quando a página de login é dispensada, com uma tela que diz "You did it!". Ótimo, Mas agora o usuário não pode realizar nenhuma outra ação e também não sabe em que parte do app está. Vamos adicionar a navegação para resolver isso.

O Material Design oferece padrões de navegação que garantem uma boa usabilidade. Um dos componentes mais visíveis é a barra de apps superior.

Para que os usuários possam navegar e ter acesso rápido a outras ações, vamos adicionar uma barra de apps superior.

Adicionar um widget AppBar

Em shr_product_grid_fragment.xml, exclua o bloco <LinearLayout> que contém a mensagem "Você conseguiu!" TextView e substitua pelo seguinte:

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>

O shr_product_grid_fragment.xml agora vai ficar assim:

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>

Muitas barras de apps têm um botão ao lado do título. Vamos adicionar um ícone de menu no nosso.

Adicionar um ícone de navegação

Ainda no shr_product_grid_fragment.xml, adicione o seguinte ao componente XML Toolbar que você acabou de adicionar ao layout:

shr_product_grid_fragment.xml

app:navigationIcon="@drawable/shr_menu"

O shr_product_grid_fragment.xml vai ficar assim:

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>

Adicionar botões de ação e definir o estilo da barra de apps superior

Também é possível adicionar botões ao final da barra de apps. No Android, são chamados de botões de ação. Vamos estilizar a barra de apps superior e adicionar botões de ação ao menu de forma programática.

Na função onCreateView do ProductGridFragment.kt, defina o Toolbar do activity para ser usado como um ActionBar usando setSupportActionBar. Você pode fazer isso depois que a visualização tiver sido criada com 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;
}

Em seguida, logo abaixo do método que acabamos de mudar para configurar a barra de ferramentas, vamos substituir onCreateOptionsMenu para inflar o conteúdo de shr_toolbar_menu.xml na barra de ferramentas:

ProductGridFragment.kt

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

Por fim, substitua onCreate() em ProductGridFragment.kt e, depois de chamar super(), chame setHasOptionMenu com true:

ProductGridFragment.kt

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

Os snippets de código acima definem a barra de apps do layout XML como a barra de ações dessa atividade. O callback onCreateOptionsMenu informa à atividade o que usar como nosso menu. Nesse caso, ele vai colocar os itens de menu de R.menu.shr_toolbar_menu na barra de apps. O arquivo de menu contém dois itens: "Pesquisar" e em "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>

Depois dessas mudanças, o arquivo ProductGridFragment.kt ficará assim:

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)
   }
}

Crie e execute. A tela inicial ficará assim:

d04e8aa3b27f4754.png

Agora, a barra de ferramentas tem um ícone de navegação, um título e dois ícones de ação no lado direito. A barra de ferramentas também mostra a elevação usando uma sombra sutil, mostrando que ela está em uma camada diferente do conteúdo.

4. Adicionar um cartão

Agora que nosso app tem uma estrutura, vamos organizar o conteúdo colocando-o em cards.

Adicionar um cartão

Para começar, vamos adicionar um card abaixo da barra de apps superior. Um card precisa ter uma região para uma imagem, um título e um rótulo para texto secundário. Adicione o seguinte em shr_product_grid_fragment.xml abaixo 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>

Crie e execute:

f6184a55ccb5f920.png

Nesta prévia, é possível ver que o card é inserido da borda esquerda e tem cantos arredondados e uma sombra (que expressa a elevação dele). O elemento inteiro é chamado de "contêiner". Todos os elementos dentro dele são opcionais, exceto o contêiner.

Você pode adicionar os seguintes elementos a um contêiner: texto do cabeçalho, miniatura ou avatar, texto do subtítulo, divisores e até mesmo botões e ícones. O card que acabamos de criar, por exemplo, contém dois TextViews (um para o título e outro para o texto secundário) em um LinearLayout alinhado na parte de baixo do card.

Os cards geralmente são exibidos com outros em uma coleção. Na próxima seção deste codelab, eles serão apresentados como uma coleção em uma grade.

5. Criar uma grade de cards

Quando há vários cards em uma tela, eles são agrupados em uma ou mais coleções. Os cards em uma grade são coplanares, o que significa que eles compartilham a mesma elevação em repouso que os outros (a menos que sejam levantados ou arrastados, mas não abordaremos isso neste codelab).

Configurar a grade de cards

Confira o arquivo shr_product_card.xml que fornecemos para você:

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>

Esse layout de card contém um card com uma imagem (neste caso, um NetworkImageView, que permite carregar e mostrar imagens de um URL) e dois TextViews.

Em seguida, consulte a ProductCardRecyclerViewAdapter que fornecemos para você. Está no mesmo pacote 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
   }
}

A classe do adaptador acima gerencia o conteúdo da grade. Para determinar o que cada visualização precisa fazer com o próprio conteúdo, em breve vamos programar o código para onBindViewHolder().

No mesmo pacote, também é possível dar uma olhada em ProductCardViewHolder. Essa classe armazena as visualizações que afetam o layout do cartão para que possamos modificá-las mais tarde.

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 nossa grade, primeiro remova o marcador de posição MaterialCardView de shr_product_grid_fragment.xml. Em seguida, adicione o componente que representa nossa grade de cards. Neste caso, vamos usar uma RecyclerView. Adicione o componente RecyclerView ao shr_product_grid_fragment.xml abaixo do 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>

O shr_product_grid_fragment.xml vai ficar assim:

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 fim, em onCreateView(), adicione o código de inicialização RecyclerView em ProductGridFragment.kt depois de chamar setUpToolbar(view) e antes da instrução 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;
}

O snippet de código acima contém as etapas de inicialização necessárias para configurar um RecyclerView. Isso inclui a configuração do gerenciador de layout do RecyclerView, além da inicialização e configuração do adaptador do RecyclerView.

Seu arquivo ProductGridFragment.kt agora terá esta aparência:

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)
   }
}

Crie e execute:

f9aeab846fc3bb4c.png

Agora os cards estão lá! Eles ainda não mostram nada, então vamos adicionar alguns dados de produtos.

Adicionar imagens e texto

Para cada card, adicione uma imagem, o nome do produto e o preço. Nossa abstração ViewHolder contém as visualizações de cada card. No ViewHolder, adicione as três visualizações desta maneira.

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)
}

Atualize o método onBindViewHolder() no ProductCardRecyclerViewAdapter para definir o título, o preço e a imagem do produto para cada visualização do produto, conforme mostrado abaixo:

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)
   }
}

O código acima informa ao adaptador da RecyclerView o que fazer com cada card usando um ViewHolder.

Aqui, ele define os dados de texto em cada um dos TextViews da ViewHolder e chama um ImageRequester para receber uma imagem de um URL. A ImageRequester é uma classe que disponibilizamos para sua conveniência e usa a biblioteca Volley. Esse é um tema fora do escopo deste codelab, mas fique à vontade para explorar o código por conta própria.

Crie e execute:

249db074eff043f4.png

Agora nossos produtos estão aparecendo no app.

6. Recapitulação

Nosso app tem um fluxo básico que leva o usuário da tela de login a uma tela inicial, onde os produtos são exibidos. Em apenas algumas linhas de código, adicionamos uma barra de apps superior com um título e três botões, além de uma grade de cards para apresentar o conteúdo do nosso app. Nossa tela inicial agora é simples e funcional, com uma estrutura básica e um conteúdo acionável.

Próximas etapas

Com a barra de apps, o card, o campo de texto e o botão na parte de cima, usamos quatro componentes principais do Material Design da biblioteca MDC-Android. Para explorar ainda mais componentes, acesse o catálogo do MDC-Android.

Embora seja totalmente funcional, nosso app ainda não expressa nenhuma marca ou estilo específico. No codelab MDC-103: temas do Material Design usando cor, forma, elevação e tipo, vamos personalizar o estilo desses componentes para representar uma marca vibrante e moderna.

Este codelab exigiu esforço e tempo normais para ser concluído

Concordo totalmente Concordo Não concordo nem discordo Discordo Discordo totalmente

Quero continuar usando componentes do Material Design no futuro

Concordo totalmente Concordo Neutro Discordo Discordo totalmente