Este codelab faz parte do curso Conceitos básicos do Kotlin para Android. Você vai aproveitar mais este curso se fizer os codelabs em sequência. Todos os codelabs do curso estão listados na página inicial dos codelabs de princípios básicos do Kotlin para Android.
Introdução
Este codelab ensina a usar um RecyclerView para mostrar listas de itens. Com base no app de rastreamento do sono da série anterior de codelabs, você vai aprender uma maneira melhor e mais versátil de mostrar dados usando um RecyclerView com uma arquitetura recomendada.
O que você já precisa saber
Você precisa:
- Criar uma interface do usuário (UI) básica usando uma atividade, fragmentos e visualizações.
- Navegar entre fragmentos e usar
safeArgspara transmitir dados entre eles. - Usando ViewModels, fábricas de ViewModels, transformações,
LiveDatae os observadores deles. - Criar um banco de dados
Room, um DAO e definir entidades. - Usar corrotinas para tarefas de banco de dados e outras tarefas de longa duração.
O que você vai aprender
- Como usar um
RecyclerViewcom umAdaptere umViewHolderpara mostrar uma lista de itens.
Atividades deste laboratório
- Mude o app TrackMySleepQuality da lição anterior para usar um
RecyclerViewe mostrar os dados de qualidade do sono.
Neste codelab, você vai criar a parte RecyclerView de um app que monitora a qualidade do sono. O app usa um banco de dados Room para armazenar dados de sono ao longo do tempo.
O app rastreador de sono inicial tem duas telas, representadas por fragmentos, conforme mostrado na figura abaixo.

A primeira tela, mostrada à esquerda, tem botões para iniciar e parar o rastreamento. Essa tela também mostra todos os dados de sono do usuário. O botão Limpar exclui permanentemente todos os dados que o app coletou do usuário. A segunda tela, mostrada à direita, é para selecionar uma classificação de qualidade do sono.
Esse app usa uma arquitetura simplificada com um controlador de UI, ViewModel e LiveData. O app também usa um banco de dados Room para tornar os dados de sono persistentes.

A lista de noites de sono mostrada na primeira tela é funcional, mas não é bonita. O app usa um formatador complexo para criar strings de texto para a visualização de texto e números para a qualidade. Além disso, esse design não é escalonável. Depois de corrigir todos esses problemas neste codelab, o app final terá a mesma funcionalidade, e a tela principal vai ficar assim:

Mostrar uma lista ou grade de dados é uma das tarefas mais comuns da interface do usuário no Android. As listas variam de simples a muito complexas. Uma lista de visualizações de texto pode mostrar dados simples, como uma lista de compras. Uma lista complexa, como uma lista anotada de destinos de férias, pode mostrar ao usuário muitos detalhes em uma grade rolável com cabeçalhos.
Para oferecer suporte a todos esses casos de uso, o Android fornece o widget RecyclerView.

O maior benefício do RecyclerView é que ele é muito eficiente para listas grandes:
- Por padrão, a
RecyclerViewsó funciona para processar ou renderizar itens que estão visíveis na tela no momento. Por exemplo, se a lista tiver mil elementos, mas apenas 10 estiverem visíveis, aRecyclerViewrealiza o trabalho suficiente para desenhar apenas os 10 itens na tela. Quando o usuário rola a página, oRecyclerViewdescobre quais itens precisam estar na tela e faz o trabalho suficiente para exibi-los. - Quando um item rola para fora da tela, as visualizações dele são recicladas. Isso significa que o item é preenchido com um novo conteúdo que aparece na tela. Esse comportamento do
RecyclerVieweconomiza muito tempo de processamento e ajuda as listas a rolarem de maneira fluida. - Quando um item muda, em vez de redesenhar a lista inteira, o
RecyclerViewpode atualizar apenas esse item. Isso é um grande ganho de eficiência ao mostrar listas de itens complexos.
Na sequência mostrada abaixo, é possível ver que uma visualização foi preenchida com dados, ABC. Depois que essa visualização rola para fora da tela, o RecyclerView a reutiliza para novos dados, XYZ.
O padrão de adaptador
Se você viaja entre países que usam tomadas diferentes, provavelmente sabe como conectar seus dispositivos usando um adaptador. O adaptador permite converter um tipo de plugue em outro, o que realmente converte uma interface em outra.
O padrão de adaptador na engenharia de software ajuda um objeto a trabalhar com outra API. O RecyclerView usa um adaptador para transformar os dados do app em algo que o RecyclerView possa mostrar, sem mudar a forma como o app armazena e processa os dados. Para o app de monitoramento do sono, você cria um adaptador que adapta os dados do banco de dados Room para algo que o RecyclerView sabe como mostrar, sem mudar o ViewModel.
Implementar um RecyclerView

Para mostrar seus dados em um RecyclerView, você precisa das seguintes partes:
- Dados a serem mostrados.
- Uma instância
RecyclerViewdefinida no arquivo de layout para atuar como o contêiner das visualizações. - Um layout para um item de dados.
Se todos os itens da lista forem iguais, você poderá usar o mesmo layout para todos eles, mas isso não é obrigatório. O layout do item precisa ser criado separadamente do layout do fragmento para que uma visualização de item por vez possa ser criada e preenchida com dados. - Um gerenciador de layout.
O gerenciador de layout processa a organização (o layout) dos componentes de UI em uma visualização. - Um fixador de visualização.
O fixador de visualização estende a classeViewHolder. Ele contém as informações de visualização para mostrar um item do layout. Os armazenadores de visualização também adicionam informações que oRecyclerViewusa para mover as visualizações pela tela de maneira eficiente. - Um adaptador.
O adaptador conecta seus dados aoRecyclerView. Ele adapta os dados para que possam ser mostrados em umViewHolder. UmRecyclerViewusa o adaptador para descobrir como mostrar os dados na tela.
Nesta tarefa, você vai adicionar uma RecyclerView ao arquivo de layout e configurar um Adapter para expor os dados de sono à RecyclerView.
Etapa 1: adicionar RecyclerView com LayoutManager
Nesta etapa, você vai substituir o ScrollView por um RecyclerView no arquivo fragment_sleep_tracker.xml.
- Faça o download do app RecyclerViewFundamentals-Starter no GitHub.
- Crie e execute o app. Observe como os dados são mostrados como texto simples.
- Abra o arquivo de layout
fragment_sleep_tracker.xmlna guia Design do Android Studio. - No painel Component Tree, exclua o
ScrollView. Essa ação também exclui oTextViewque está dentro doScrollView. - No painel Palette, role a lista de tipos de componentes à esquerda para encontrar Containers e selecione essa opção.
- Arraste um
RecyclerViewdo painel Paleta para o painel Árvore de componentes. Coloque oRecyclerViewdentro doConstraintLayout.

- Se uma caixa de diálogo perguntar se você quer adicionar uma dependência, clique em OK para permitir que o Android Studio adicione a dependência
recyclerviewao arquivo do Gradle. Isso pode levar alguns segundos, e depois o app será sincronizado.

- Abra o arquivo
build.gradledo módulo, role até o final e observe a nova dependência, que é semelhante ao código abaixo:
implementation 'androidx.recyclerview:recyclerview:1.0.0'
- Volte para
fragment_sleep_tracker.xml. - Na guia Texto, procure o código
RecyclerViewmostrado abaixo:
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent" />- Atribua um
iddesleep_listàRecyclerView.
android:id="@+id/sleep_list"- Posicione o
RecyclerViewpara ocupar a parte restante da tela dentro doConstraintLayout. Para fazer isso, restrinja a parte de cima doRecyclerViewao botão Start, a parte de baixo ao botão Clear e cada lado ao elemento pai. Defina a largura e a altura do layout como 0 dp no Layout Editor ou em XML usando o seguinte código:
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/clear_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/stop_button"- Adicione um gerenciador de layout ao XML
RecyclerView. CadaRecyclerViewprecisa de um gerenciador de layout que informe como posicionar os itens na lista. O Android oferece umLinearLayoutManager, que por padrão organiza os itens em uma lista vertical de linhas de largura total.
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"- Mude para a guia Design e observe que as restrições adicionadas fizeram com que o
RecyclerViewse expandisse para preencher o espaço disponível.

Etapa 2: criar o layout do item da lista e o suporte da visualização de texto
O RecyclerView é apenas um contêiner. Nesta etapa, você cria o layout e a infraestrutura para os itens serem exibidos no RecyclerView.
Para chegar a um RecyclerView funcional o mais rápido possível, use inicialmente um item de lista simplista que mostre apenas a qualidade do sono como um número. Para isso, você precisa de um suporte de visualização, TextItemViewHolder. Você também precisa de uma visualização, um TextView, para os dados. Em uma etapa posterior, você vai saber mais sobre os titulares de visualização e como apresentar todos os dados de sono.
- Crie um arquivo de layout chamado
text_item_view.xml. Não importa o que você usa como elemento raiz, porque vai substituir o código do modelo. - Em
text_item_view.xml, exclua todo o código fornecido. - Adicione um
TextViewcom padding de16dpno início e no fim e um tamanho de texto de24sp. Deixe a largura corresponder ao elemento pai e a altura agrupar o conteúdo. Como essa visualização é mostrada dentro doRecyclerView, não é necessário colocar a visualização em umViewGroup.
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:textSize="24sp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />- Abra
Util.kt. Role até o final e adicione a definição mostrada abaixo, que cria a classeTextItemViewHolder. Coloque o código na parte de baixo do arquivo, depois da última chave de fechamento. O código vai emUtil.ktporque esse holder de visualização é temporário e será substituído mais tarde.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)- Se solicitado, importe
android.widget.TextVieweandroidx.recyclerview.widget.RecyclerView.
Etapa 3: criar SleepNightAdapter
A principal tarefa ao implementar uma RecyclerView é criar o adaptador. Você tem um armazenador de visualização simples para a visualização de itens e um layout para cada item. Agora você pode criar um adaptador. O adaptador cria um fixador de visualização e o preenche com dados para que o RecyclerView seja exibido.
- No pacote
sleeptracker, crie uma nova classe Kotlin com o nomeSleepNightAdapter. - Faça a classe
SleepNightAdapterestenderRecyclerView.Adapter. A classe é chamada deSleepNightAdapterporque adapta um objetoSleepNightem algo queRecyclerViewpode usar. O adaptador precisa saber qual fixador de visualização usar. Portanto, transmitaTextItemViewHolder. Importe os componentes necessários quando solicitado. Em seguida, um erro vai aparecer porque há métodos obrigatórios a serem implementados.
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}- No nível superior de
SleepNightAdapter, crie uma variávellistOfSleepNightpara armazenar os dados.
var data = listOf<SleepNight>()- Em
SleepNightAdapter, substituagetItemCount()para retornar o tamanho da lista de noites de sono emdata. ORecyclerViewprecisa saber quantos itens o adaptador tem para mostrar, e isso é feito chamandogetItemCount().
override fun getItemCount() = data.size- Em
SleepNightAdapter, substitua a funçãoonBindViewHolder(), conforme mostrado abaixo.
A funçãoonBindViewHolder()é chamada porRecyclerViewpara mostrar os dados de um item da lista na posição especificada. Portanto, o métodoonBindViewHolder()usa dois argumentos: um fixador de visualização e uma posição dos dados a serem vinculados. Para este app, o marcador éTextItemViewHolder, e a posição é a posição na lista.
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}- Em
onBindViewHolder(), crie uma variável para um item em uma determinada posição nos dados.
val item = data[position]- O
ViewHolderque você criou tem uma propriedade chamadatextView. EmonBindViewHolder(), defina otextdatextViewcomo o número da qualidade do sono. Esse código mostra apenas uma lista de números, mas esse exemplo simples permite que você veja como o adaptador coloca os dados no holder de visualização e na tela.
holder.textView.text = item.sleepQuality.toString()- Em
SleepNightAdapter, substitua e implementeonCreateViewHolder(), que é chamado quando oRecyclerViewprecisa de um fixador de visualização para representar um item.
Essa função usa dois parâmetros e retorna umViewHolder. O parâmetroparent, que é o grupo de visualizações que contém o holder, é sempre oRecyclerView. O parâmetroviewTypeé usado quando há várias visualizações no mesmoRecyclerView. Por exemplo, se você colocar uma lista de visualizações de texto, uma imagem e um vídeo no mesmoRecyclerView, a funçãoonCreateViewHolder()precisará saber qual tipo de visualização usar.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}- Em
onCreateViewHolder(), crie uma instância deLayoutInflater.
O inflador de layout sabe como criar visualizações com layouts XML. Ocontextcontém informações sobre como inflar a visualização corretamente. Em um adaptador para uma visualização de reciclagem, sempre transmita o contexto do grupo de visualizaçãoparent, que é oRecyclerView.
val layoutInflater = LayoutInflater.from(parent.context)- Em
onCreateViewHolder(), crie oviewpedindo aolayoutinflaterpara inflar.
Transmita o layout XML da visualização e o grupo de visualizaçõesparent. O terceiro argumento, booleano, éattachToRoot. Esse argumento precisa serfalse, porque oRecyclerViewadicionará esse item à hierarquia de visualização no momento certo.
val view = layoutInflater
.inflate(R.layout.text_item_view, parent, false) as TextView- Em
onCreateViewHolder(), retorne umTextItemViewHolderfeito comview.
return TextItemViewHolder(view)- O adaptador precisa informar ao
RecyclerViewquando odatamuda, porque oRecyclerViewnão sabe nada sobre os dados. Ele só conhece os viewholders que o adaptador fornece.
Para informar aoRecyclerViewquando os dados que ele está mostrando mudarem, adicione um setter personalizado à variáveldatana parte de cima da classeSleepNightAdapter. No setter, atribua um novo valor adatae chamenotifyDataSetChanged()para acionar a nova renderização da lista com os novos dados.
var data = listOf<SleepNight>()
set(value) {
field = value
notifyDataSetChanged()
}Etapa 4: informar o RecyclerView sobre o adaptador
O RecyclerView precisa saber qual adaptador usar para receber fixadores de visualização.
- Abra
SleepTrackerFragment.kt. - Em
onCreateview(), crie um adaptador. Coloque esse código depois da criação do modeloViewModele antes da instruçãoreturn.
val adapter = SleepNightAdapter()- Associe o
adapteraoRecyclerView.
binding.sleepList.adapter = adapter- Limpe e recrie o projeto para atualizar o objeto
binding.
Se os erros relacionados abinding.sleepListoubinding.FragmentSleepTrackerBindingpersistirem, invalide os caches e reinicie. Selecione Arquivo > Invalidar caches / Reiniciar.
Se você executar o app agora, não haverá erros, mas nenhum dado será exibido quando você tocar em Iniciar e depois em Parar.
Etapa 5: inserir dados no adaptador
Até agora, você tem um adaptador e uma maneira de transferir dados do adaptador para a RecyclerView. Agora você precisa inserir dados no adaptador do ViewModel.
- Abra
SleepTrackerViewModel. - Encontre a variável
nights, que armazena todas as noites de sono, ou seja, os dados a serem mostrados. A variávelnightsé definida chamandogetAllNights()no banco de dados. - Remova
privatedenights, porque você vai criar um observador que precisa acessar essa variável. Sua declaração precisa ser semelhante a esta:
val nights = database.getAllNights()- No pacote
database, abraSleepDatabaseDao. - Encontre a função
getAllNights(). Essa função retorna uma lista de valoresSleepNightcomoLiveData. Isso significa que a variávelnightscontémLiveData, que é mantido atualizado porRoom, e você pode observarnightspara saber quando ele muda. - Abra
SleepTrackerFragment. - Em
onCreateView(), abaixo da criação doadapter, crie um observador na variávelnights.
Ao fornecer oviewLifecycleOwnerdo fragmento como proprietário do ciclo de vida, você garante que esse observador só fique ativo quando oRecyclerViewestiver na tela.
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
})- Dentro do observador, sempre que você receber um valor não nulo (para
nights), atribua o valor aodatado adaptador. Este é o código concluído para o observador e a definição dos dados:
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.data = it
}
})- Crie e execute o código.
Se o adaptador estiver funcionando, você vai ver os números de qualidade do sono em uma lista. A captura de tela à esquerda mostra -1 depois que você toca em Iniciar. A captura de tela à direita mostra o número atualizado da qualidade do sono depois que você toca em Parar e seleciona uma classificação de qualidade.

Etapa 6: saiba como os titulares de visualização são reciclados
O RecyclerView recicla os fixadores de visualização, o que significa que ele os reutiliza. À medida que uma visualização rola para fora da tela, o RecyclerView a reutiliza para a visualização que está prestes a rolar para dentro da tela.
Como esses titulares de visualização são reciclados, verifique se onBindViewHolder() define ou redefine as personalizações que os itens anteriores podem ter definido em um titular de visualização.
Por exemplo, você pode definir a cor do texto como vermelho em elementos de visualização que contêm classificações de qualidade menores ou iguais a 1 e representam sono ruim.
- Na classe
SleepNightAdapter, adicione o seguinte código ao final deonBindViewHolder().
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
}- Execute o app.
- Adicione alguns dados de baixa qualidade de sono, e o número fica vermelho.
- Adicione classificações altas para a qualidade do sono até que um número vermelho alto apareça na tela.
Como oRecyclerViewreutiliza titulares de visualização, ele acaba reutilizando um dos titulares de visualização vermelhos para uma classificação de alta qualidade. A classificação alta é mostrada erroneamente em vermelho.

- Para corrigir isso, adicione uma instrução
elsepara definir a cor como preta se a qualidade não for menor ou igual a um.
Com as duas condições explícitas, o holder de visualização vai usar a cor de texto correta para cada item.
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
} else {
// reset
holder.textView.setTextColor(Color.BLACK) // black
}- Execute o app. Os números sempre terão a cor correta.
Parabéns! Agora você tem um RecyclerView básico totalmente funcional.
Nesta tarefa, você vai substituir o suporte de visualização simples por um que possa mostrar mais dados de uma noite de sono.
O ViewHolder simples que você adicionou ao Util.kt envolve um TextView em um TextItemViewHolder.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)Então, por que o RecyclerView não usa um TextView diretamente? Essa única linha de código oferece muita funcionalidade. Um ViewHolder descreve uma visualização de item e metadados sobre o lugar dele no RecyclerView. O RecyclerView depende dessa funcionalidade para posicionar corretamente a visualização à medida que a lista rola e para fazer coisas interessantes, como animar visualizações quando itens são adicionados ou removidos no Adapter.
Se RecyclerView precisar acessar as visualizações armazenadas no ViewHolder, ele poderá fazer isso usando a propriedade itemView do fixador de visualização. O RecyclerView usa o itemView ao vincular um item para exibição na tela, ao desenhar decorações ao redor de uma visualização, como uma borda, e para implementar a acessibilidade.
Etapa 1: criar o layout do item
Nesta etapa, você vai criar o arquivo de layout para um item. O layout consiste em um ConstraintLayout com um ImageView para a qualidade do sono, um TextView para a duração do sono e um TextView para a qualidade como texto. Como você já fez layouts antes, copie e cole o código XML fornecido.
- Crie um arquivo de recursos de layout e nomeie-o como
list_item_sleep_night. - Substitua todo o código no arquivo pelo código abaixo. Depois, familiarize-se com o layout que você acabou de criar.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content">
<ImageView
android:id="@+id/quality_image"
android:layout_width="@dimen/icon_size"
android:layout_height="60dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@drawable/ic_sleep_5" />
<TextView
android:id="@+id/sleep_length"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/quality_image"
app:layout_constraintTop_toTopOf="@+id/quality_image"
tools:text="Wednesday" />
<TextView
android:id="@+id/quality_string"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="@+id/sleep_length"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/sleep_length"
app:layout_constraintTop_toBottomOf="@+id/sleep_length"
tools:text="Excellent!!!" />
</androidx.constraintlayout.widget.ConstraintLayout>- Mude para a guia Design no Android Studio. Na visualização de design, seu layout vai ficar parecido com a captura de tela à esquerda abaixo. Na visualização de planta, ele aparece como na captura de tela à direita.

Etapa 2: criar ViewHolder
- Abra
SleepNightAdapter.kt. - Crie uma classe dentro de
SleepNightAdapterchamadaViewHoldere faça com que ela estendaRecyclerView.ViewHolder.
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}- Dentro de
ViewHolder, acesse referências às visualizações. Você precisa de uma referência às visualizações que esseViewHoldervai atualizar. Sempre que você vincula esseViewHolder, é necessário acessar a imagem e as duas visualizações de texto. Você vai converter esse código para usar a vinculação de dados mais tarde.
val sleepLength: TextView = itemView.findViewById(R.id.sleep_length)
val quality: TextView = itemView.findViewById(R.id.quality_string)
val qualityImage: ImageView = itemView.findViewById(R.id.quality_image)Etapa 3: usar o ViewHolder em SleepNightAdapter
- Na definição de
SleepNightAdapter, em vez deTextItemViewHolder, use oSleepNightAdapter.ViewHolderque você acabou de criar.
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {Atualize onCreateViewHolder():
- Mude a assinatura de
onCreateViewHolder()para retornar oViewHolder. - Mude o inflador de layout para usar o recurso de layout correto,
list_item_sleep_night. - Remova a transmissão para
TextView. - Em vez de retornar um
TextItemViewHolder, retorne umViewHolder.
Confira a funçãoonCreateViewHolder()atualizada:
override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater =
LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night,
parent, false)
return ViewHolder(view)
}Atualize onBindViewHolder():
- Mude a assinatura de
onBindViewHolder()para que o parâmetroholderseja umViewHolderem vez de umTextItemViewHolder. - Em
onBindViewHolder(), exclua todo o código, exceto a definição deitem. - Defina um
valresque contenha uma referência aoresourcespara essa visualização.
val res = holder.itemView.context.resources- Defina o texto da visualização de texto
sleepLengthcomo a duração. Copie o código abaixo, que chama uma função de formatação fornecida com o código inicial.
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)- Isso gera um erro porque
convertDurationToFormatted()precisa ser definido. AbraUtil.kte remova a marca de comentário do código e das importações associadas. (Selecione Code > Comment with Line comments.) - De volta ao
onBindViewHolder(), useconvertNumericQualityToString()para definir a qualidade.
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)- Talvez seja necessário importar essas funções manualmente.
import com.example.android.trackmysleepquality.convertDurationToFormatted
import com.example.android.trackmysleepquality.convertNumericQualityToString- Defina o ícone correto para a qualidade. O novo ícone
ic_sleep_activeé fornecido no código inicial.
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})- Confira a função
onBindViewHolder()atualizada e concluída, definindo todos os dados paraViewHolder:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = data[position]
val res = holder.itemView.context.resources
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}- Execute o app. A tela vai ficar parecida com a captura de tela abaixo, mostrando o ícone de qualidade do sono, além do texto com a duração e a qualidade do sono.

O RecyclerView foi concluído. Você aprendeu a implementar um Adapter e um ViewHolder, e os juntou para mostrar uma lista com um RecyclerView Adapter.
Até agora, seu código mostra o processo de criação de um adaptador e um fixador de visualização. No entanto, é possível melhorar esse código. O código para mostrar e o código para gerenciar titulares de visualização estão misturados, e onBindViewHolder() sabe detalhes sobre como atualizar o ViewHolder.
Em um app de produção, você pode ter vários titulares de visualização, adaptadores mais complexos e vários desenvolvedores fazendo mudanças. Estruture o código para que tudo relacionado a um armazenador de visualização esteja apenas nele.
Etapa 1: refatorar onBindViewHolder()
Nesta etapa, você vai refatorar o código e mover toda a funcionalidade do holder de visualização para o ViewHolder. O objetivo dessa refatoração não é mudar a aparência do app para o usuário, mas tornar mais fácil e seguro para os desenvolvedores trabalharem no código. Felizmente, o Android Studio tem ferramentas para ajudar.
- Em
SleepNightAdapter, emonBindViewHolder(), selecione tudo, exceto a instrução para declarar a variávelitem. - Clique com o botão direito do mouse e selecione Refactor > Extract > Function.
- Nomeie a função
binde aceite os parâmetros sugeridos. Clique em OK.
A funçãobind()é colocada abaixo deonBindViewHolder().
private fun bind(holder: ViewHolder, item: SleepNight) {
val res = holder.itemView.context.resources
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
holder.quality.text = convertNumericQualityToString(item.sleepQuality, res)
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}- Coloque o cursor na palavra
holderdo parâmetroholderdebind(). PressioneAlt+Enter(Option+Enterem um Mac) para abrir o menu de intenção. Selecione Converter parâmetro em receptor para transformar em uma função de extensão com a seguinte assinatura:
private fun ViewHolder.bind(item: SleepNight) {...}- Recorte e cole a função
bind()noViewHolder. - Torne
bind()público. - Importe
bind()para o adaptador, se necessário. - Como agora ele está no
ViewHolder, você pode remover a parteViewHolderda assinatura. Confira o código final da funçãobind()na classeViewHolder.
fun bind(item: SleepNight) {
val res = itemView.context.resources
sleepLength.text = convertDurationToFormatted(
item.startTimeMilli, item.endTimeMilli, res)
quality.text = convertNumericQualityToString(
item.sleepQuality, res)
qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}Etapa 2: refatorar onCreateViewHolder
O método onCreateViewHolder() no adaptador atualmente infla a visualização do recurso de layout para o ViewHolder. No entanto, a inflação não tem nada a ver com o adaptador, mas sim com o ViewHolder. A inflação deve ocorrer no ViewHolder.
- Em
onCreateViewHolder(), selecione todo o código no corpo da função. - Clique com o botão direito do mouse e selecione Refactor > Extract > Function.
- Nomeie a função
frome aceite os parâmetros sugeridos. Clique em OK. - Coloque o cursor no nome da função
from. PressioneAlt+Enter(Option+Enterem um Mac) para abrir o menu de intenção. - Selecione Mover para objeto complementar. A função
from()precisa estar em um objeto complementar para que possa ser chamada na classeViewHolder, não em uma instânciaViewHolder. - Mova o objeto
companionpara a classeViewHolder. - Torne
from()público. - Em
onCreateViewHolder(), mude a instruçãoreturnpara retornar o resultado da chamada defrom()na classeViewHolder.
Os métodosonCreateViewHolder()efrom()concluídos devem ficar parecidos com o código abaixo, e seu código precisa ser criado e executado sem erros.
override fun onCreateViewHolder(parent: ViewGroup, viewType:
Int): ViewHolder {
return ViewHolder.from(parent)
}companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night, parent, false)
return ViewHolder(view)
}
}- Mude a assinatura da classe
ViewHolderpara que o construtor seja particular. Comofrom()agora é um método que retorna uma nova instânciaViewHolder, não há mais motivo para alguém chamar o construtor deViewHolder.
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){- Execute o app. Ele será criado e executado da mesma forma que antes, que é o resultado desejado após a refatoração.
Projeto do Android Studio: RecyclerViewFundamentals (link em inglês)
- Mostrar uma lista ou grade de dados é uma das tarefas mais comuns da interface do usuário no Android. O
RecyclerViewfoi projetado para ser eficiente mesmo ao exibir listas extremamente grandes. RecyclerViewsó faz o trabalho necessário para processar ou renderizar itens que estão visíveis na tela no momento.- Quando um item rola para fora da tela, as visualizações dele são recicladas. Isso significa que o item é preenchido com um novo conteúdo que aparece na tela.
- O padrão de adaptador na engenharia de software ajuda um objeto a trabalhar com outra API. O
RecyclerViewusa um adaptador para transformar os dados do app em algo que possa ser exibido, sem precisar mudar a forma como o app armazena e processa os dados.
Para mostrar seus dados em um RecyclerView, você precisa das seguintes partes:
- RecyclerView
Para criar uma instância deRecyclerView, defina um elemento<RecyclerView>no arquivo de layout. - LayoutManager
UmRecyclerViewusa umLayoutManagerpara organizar o layout dos itens noRecyclerView, como em uma grade ou em uma lista linear.
No<RecyclerView>do arquivo de layout, defina o atributoapp:layoutManagercomo o gerenciador de layout (comoLinearLayoutManagerouGridLayoutManager).
Também é possível definir oLayoutManagerpara umRecyclerViewde maneira programática. Essa técnica será abordada em um codelab posterior. - Layout para cada item
Crie um layout para um item de dados em um arquivo de layout XML. - Adapter
: crie um adaptador que prepare os dados e como eles serão mostrados em umViewHolder. Associe o adaptador aoRecyclerView.
Quando oRecyclerViewé executado, ele usa o adaptador para descobrir como mostrar os dados na tela.
O adaptador exige que você implemente os seguintes métodos:
–getItemCount()para retornar o número de itens.
–onCreateViewHolder()para retornar oViewHolderde um item na lista.
–onBindViewHolder()para adaptar os dados às visualizações de um item na lista. - ViewHolder
UmViewHoldercontém as informações da visualização para mostrar um item do layout dele. - O método
onBindViewHolder()no adaptador adapta os dados às visualizações. Você sempre substitui esse método. Normalmente, oonBindViewHolder()aumenta o layout de um item e coloca os dados nas visualizações do layout. - Como o
RecyclerViewnão sabe nada sobre os dados, oAdapterprecisa informar oRecyclerViewquando esses dados mudam. UsenotifyDataSetChanged()para notificar oAdapterde que os dados mudaram.
Curso da Udacity:
Documentação do desenvolvedor Android:
Esta seção lista as possíveis atividades de dever de casa para os alunos que estão fazendo este codelab como parte de um curso ministrado por um professor. Cabe ao professor fazer o seguinte:
- Atribuir o dever de casa, se necessário.
- Informar aos alunos como enviar deveres de casa.
- Atribuir nota aos deveres de casa.
Os professores podem usar essas sugestões o quanto quiserem, podendo passar os exercícios que acharem mais apropriados como dever de casa.
Se você estiver seguindo este codelab por conta própria, sinta-se à vontade para usar esses deveres de casa para testar seu conhecimento.
Responda estas perguntas
Pergunta 1
Como o RecyclerView exibe os itens? Selecione todas as opções aplicáveis.
▢ Exibe itens em uma lista ou grade.
▢ Rola vertical ou horizontalmente.
▢ Rola diagonalmente em dispositivos maiores, como tablets.
▢ Permite usar layouts personalizados quando uma lista ou grade não é o suficiente para o caso de uso.
Pergunta 2
Quais são os benefícios do uso do RecyclerView? Selecione todas as opções aplicáveis.
▢ Mostra listas grandes com eficiência.
▢ Atualiza os dados automaticamente.
▢ Minimiza a necessidade de mudanças quando um item é atualizado, excluído ou adicionado à lista.
▢ Reutiliza a visualização que rola para fora da tela para mostrar o próximo item que rola na tela.
Pergunta 3
Quais são alguns dos motivos para usar adaptadores? Selecione todas as opções aplicáveis.
▢ A separação de responsabilidades facilita a mudança e o teste do código.
▢ RecyclerView é independente dos dados que estão sendo mostrados.
▢ As camadas de tratamento de dados não precisam se preocupar com a forma como os dados serão exibidos.
▢ O app será executado mais rapidamente.
Pergunta 4
Quais das seguintes opções é verdadeira sobre o ViewHolder? Selecione todas as opções aplicáveis.
▢ O layout ViewHolder é definido nos arquivos XML.
▢ Há um ViewHolder para cada unidade de dados no conjunto.
▢ É possível ter mais de um ViewHolder em um RecyclerView.
▢ O Adapter vincula dados ao ViewHolder.
Comece a próxima lição: