Introdução

No tutorial anterior, você aprendeu como obter dados de um serviço da web e analisar a resposta em um objeto de dados. Neste tutorial, você se baseia nesse conhecimento para carregar e exibir fotos de um URL da web. Você também revisará como construir um RecyclerView e usá-lo para exibir uma grade de imagens na página de visão geral.

O que já deveria saber

O que aprenderá

O que fará

Neste tutorial (e tutoriais relacionados), você trabalha com um aplicativo chamado MarsRealEstate, que mostra propriedades à venda em Marte. O aplicativo se conecta a um servidor de Internet para recuperar e exibir dados de propriedade, incluindo detalhes como o preço e se a propriedade está disponível para venda ou aluguel. As imagens que representam cada propriedade são fotos da vida real de Marte, capturadas pelos robôs de Marte da NASA.

A versão do aplicativo que você constrói neste tutorial preenche a página de visão geral, que exibe uma grade de imagens. As imagens são parte dos dados de propriedade que seu aplicativo obtém do serviço da web de imóveis da Mars. Seu aplicativo usará a biblioteca Glide para carregar e exibir as imagens e um RecyclerView para criar o layout de grade para as imagens. Seu aplicativo também tratará com erros de rede normalmente.

Exibindo uma foto de um URL da web pode parecer simples, mas há um pouco de engenharia para que funcione bem. A imagem deve ser baixada, armazenada em buffer e decodificada de seu formato compactado para uma imagem que o Android possa usar. A imagem deve ser armazenada em cache na memória, um cache baseado em armazenamento ou ambos. Tudo isso precisa acontecer em threads de fundo de baixa prioridade para que a IU permaneça responsiva. Além disso, para obter o melhor desempenho da rede e da CPU, você pode buscar e decodificar mais de uma imagem de uma vez. Aprendendo como carregar imagens da rede de maneira eficaz pode ser um tutorial em si.

Felizmente, você pode usar uma biblioteca desenvolvida pela comunidade chamada Glide para baixar, armazenar em buffer, decodificar e armazenar em cache suas imagens. O Glide deixa você com muito menos trabalho do que se você tivesse que fazer tudo isso do zero.

O Glide precisa basicamente de dois argumentos:

Nesta tarefa, você aprenderá a usar o Glide para exibir uma única imagem do serviço da web de imóveis. Você exibe a imagem que representa a primeira propriedade de Marte na lista de propriedades que o serviço da web retorna. Aqui estão as capturas de tela de antes e depois:

Etapa 1: Adicione dependência Glide

  1. Abra o aplicativo MarsRealEstate do último tutorial. (Você pode baixar MarsRealEstateNetwork aqui se não tiver o aplicativo).
  2. Execute o aplicativo para ver o que ele faz. (Ele exibe detalhes de texto de uma propriedade que está hipoteticamente disponível em Marte).
  3. Abra build.gradle (Module: App).
  4. Na seção dependencies, adicione esta linha para a biblioteca Glide:
implementation "com.github.bumptech.glide:glide:$version_glide"


Observe que o número da versão já está definido separadamente no arquivo do projeto Gradle.

  1. Clique em Sync Now para reconstruir o projeto com a nova dependência.

Etapa 2: Atualize o modelo de vista

Em seguida, você atualiza a classe OverviewViewModel para incluir dados ativos para uma única propriedade de Marte.

  1. Abra overview/OverviewViewModel.kt. Logo abaixo de LiveData para a _response, adicione dados ativos internos (mutáveis) e externos (imutáveis) para um único objeto MarsProperty.

    Importe a classe MarsProperty (com.example.android.marsrealestate.network.MarsProperty) quando solicitado.
private val _property = MutableLiveData<MarsProperty>()

val property: LiveData<MarsProperty>
   get() = _property
  1. No método getMarsRealEstateProperties(), encontre a linha dentro do bloco try/catch {} que define _response.value para o número de propriedades. Adicione o teste mostrado abaixo. Se os objetos MarsProperty estiverem disponíveis, este teste define o valor de _property LiveData para a primeira propriedade em listResult.
if (listResult.size > 0) {   
    _property.value = listResult[0]
}

O bloco try/catch {} completo agora se parece com isto:

try {
   val listResult = MarsApi.retrofitService.getProperties()
   _response.value = "Success: ${listResult.size} Mars properties retrieved"
   if (listResult.size > 0) {      
       _property.value = listResult[0]
   }
 } catch (e: Exception) {
    _response.value = "Failure: ${e.message}"
 }
  1. Abra o arquivo res/layout/fragment_overview.xml. No elemento <TextView>, altere android:text para vincular ao componente imgSrcUrl da propriedade property LiveData:
android:text="@{viewModel.property.imgSrcUrl}"
  1. Execute o aplicativo. O TextView exibe apenas o URL da imagem na primeira propriedade de Marte. Tudo o que você fez até agora foi configurar o modelo de vistas e os dados ativos para esse URL.

Etapa 3: Crie um adaptador de vinculação e chame Glide

Agora você tem a URL de uma imagem para exibir e é hora de começar a trabalhar com o Glide para carregar essa imagem. Nesta etapa, você usa um adaptador de vinculação para obter a URL de um atributo XML associado a um ImageView e usa o Glide para carregar a imagem. Adaptadores de vinculação são métodos de extensão que ficam entre uma exibição e os dados associados para fornecer um comportamento personalizado quando os dados são alterados. Nesse caso, o comportamento personalizado é chamar o Glide para carregar uma imagem de um URL em um ImageView.

  1. Abra BindingAdapters.kt. Este arquivo conterá os adaptadores de vinculação que você usa em todo o aplicativo.
  2. Crie uma função bindImage() que recebe uma ImageView e uma String como parâmetros. Anote a função com @BindingAdapter. A anotação @BindingAdapter informa à vinculação de dados que você deseja que este adaptador de vinculação seja executado quando um item XML tiver o atributo imageUrl.

    Importe androidx.databinding.BindingAdapter e android.widget.ImageView quando solicitado.
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {

}
  1. Dentro da função bindImage(), adicione um bloco let {} para o argumento imgUrl:
imgUrl?.let { 
}
  1. Dentro do bloco let {}, adicione a linha mostrada abaixo para converter a string de URL (do XML) em um objeto Uri. Importe androidx.core.net.toUri quando solicitado.

    Você deseja que o objeto Uri final use o esquema HTTPS, pois o servidor que extrai as imagens de requer esse esquema. Para usar o esquema HTTPS, anexe buildUpon.scheme("https") ao construtor toUri. O método toUri() é uma função de extensão Kotlin da biblioteca principal do Android KTX, então parece que faz parte da classe String.
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
  1. Ainda dentro de let {}, chame Glide.with() para carregar a imagem do objeto Uri no ImageView. Importe com.bumptech.glide.Glide quando solicitado.
Glide.with(imgView.context)
       .load(imgUri)
       .into(imgView)

Etapa 4: Atualize o layout e os fragmentos

Embora o Glide tenha carregado a imagem, não há nada para ver ainda. A próxima etapa é atualizar o layout e os fragmentos com um ImageView para exibir a imagem.

  1. Abra res/layout/gridview_item.xml. Este é o arquivo de recurso de layout que você usará para cada item no RecyclerView posteriormente no tutorial. Você o usa temporariamente aqui para mostrar apenas uma única imagem.
  2. Acima do elemento <ImageView>, adicione um elemento <data> para a vinculação de dados e vincule à classe OverviewViewModel:
<data>
   <variable
       name="viewModel"
       type="com.example.android.marsrealestate.overview.OverviewViewModel" />
</data>
  1. Adicione um atributo app:imageUrl ao elemento ImageView para usar o novo adaptador de vinculação de carregamento de imagem:
app:imageUrl="@{viewModel.property.imgSrcUrl}"
  1. Abra overview/OverviewFragment.kt. No método onCreateView(), comente a linha que infla a classe FragmentOverviewBinding e a atribui à variável de vinculação. Isso é apenas temporário; você voltará a ele mais tarde.
//val binding = FragmentOverviewBinding.inflate(inflater)
  1. Adicione uma linha para inflar a classe GridViewItemBinding. Importe com.example.android.marsrealestate. databinding.GridViewItemBinding quando solicitado.
val binding = GridViewItemBinding.inflate(inflater)
  1. Execute o aplicativo. Agora você deve ver uma foto da imagem da primeira MarsProperty na lista de resultados.

Etapa 5: Adicione carregamento simples e imagens de erro

O Glide pode melhorar a experiência do usuário, mostrando um espaço reservado para imagem ao carregar a imagem e uma imagem de erro se o carregamento falhar, por exemplo, se a imagem estiver faltando ou corrompida. Nesta etapa, você adiciona essa funcionalidade ao adaptador de vinculação e ao layout.

  1. Abra res/drawable/ic_broken_image.xml e clique na guia Preview à direita. Para a imagem de erro, você está usando o ícone de imagem quebrada que está disponível na biblioteca de ícones integrada. Este drawable vetorial usa o atributo android:tint para colorir o ícone de cinza.

  1. Abra res/drawable/loading_animation.xml. Este drawable é uma animação definida com a etiqueta <animate-rotate>. A animação gira um drawable de imagem, loading_img.xml, em torno do ponto central. (Você não vê a animação na prévia).

  1. Retorne ao arquivo BindingAdapters.kt. No método bindImage(), atualize a chamada para Glide.with() para chamar a função apply() entre load() e into(). Importe com.bumptech.glide.request.RequestOptions quando solicitado.

    Este código define a imagem de carregamento do marcador a ser usada durante o carregamento (o drawable loading_animation). O código também define uma imagem a ser usada se o carregamento da imagem falhar (o drawable broken_image). O método bindImage() completo agora se parece com isto:
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
    imgUrl?.let {
        val imgUri = 
           imgUrl.toUri().buildUpon().scheme("https").build()
        Glide.with(imgView.context)
                .load(imgUri)
                .apply(RequestOptions()
                        .placeholder(R.drawable.loading_animation)
                        .error(R.drawable.ic_broken_image))
                .into(imgView)
    }
}
  1. Execute o aplicativo. Dependendo da velocidade da sua conexão de rede, você pode ver rapidamente a imagem de carregamento enquanto o Glide baixa e exibe a imagem da propriedade. Mas você ainda não verá o ícone da imagem quebrada, mesmo se desligar a rede - você corrige isso na última parte do tutorial.

Seu aplicativo agora carrega informações de propriedade da Internet. Usando dados do primeiro item da lista MarsProperty, você criou uma propriedade LiveData no modelo de vistas e usou o URL da imagem desses dados de propriedade para preencher um ImageView. Mas o objetivo é que seu aplicativo exiba uma grade de imagens, então você deseja usar um RecyclerView com um GridLayoutManager.

Etapa 1: Atualize o modelo de vista

No momento, o modelo de vistas tem uma _property LiveData que contém um objeto MarsProperty - o primeiro na lista de respostas do serviço da web. Nesta etapa, você altera o LiveData para conter a lista inteira de objetos MarsProperty.

  1. Abra overview/OverviewViewModel.kt.
  2. Altere a variável privada _property para _properties. Mude o tipo para uma lista de objetos MarsProperty.
private val _properties = MutableLiveData<List<MarsProperty>>()
  1. Substitua os property dados ativos externos por properties. Adicione a lista ao tipo LiveData aqui também:
 val properties: LiveData<List<MarsProperty>>
        get() = _properties
  1. Role para baixo até o método getMarsRealEstateProperties(). Dentro do bloco try {}, substitua todo o teste que você adicionou na tarefa anterior pela linha mostrada abaixo. Porque o MarsApi.retrofitService.getProperties()

retorna uma lista de objetos MarsProperty, você pode apenas atribuí-la a _properties.value em vez de testar uma resposta bem-sucedida.

_properties.value = MarsApi.retrofitService.getProperties()

O bloco try/catch inteiro agora se parece com isto:

try {
    _properties.value = MarsApi.retrofitService.getProperties()   
    _response.value = "Success: Mars properties retrieved"
} catch (e: Exception) {
   _response.value = "Failure: ${e.message}"
}

Etapa 2: Atualize os layouts e fragmentos

A próxima etapa é alterar o layout e os fragmentos do aplicativo para usar uma vista de reciclagem e um layout de grade, em vez da vista de imagem única.

  1. Abra res/layout/gridview_item.xml. Altere a vinculação de dados de OverviewViewModel para MarsProperty e renomeie a variável para "property".
<variable
   name="property"
   type="com.example.android.marsrealestate.network.MarsProperty" />
  1. Em <ImageView>, altere o atributo app:imageUrl para referir-se ao URL da imagem no objeto MarsProperty:
app:imageUrl="@{property.imgSrcUrl}"
  1. Abra overview/OverviewFragment.kt. Em onCreateview(), descomente a linha que infla FragmentOverviewBinding. Exclua ou comente a linha que infla GridViewBinding. Essas alterações desfazem as alterações temporárias feitas na última tarefa.
val binding = FragmentOverviewBinding.inflate(inflater)
 // val binding = GridViewItemBinding.inflate(inflater)
  1. Abra res/layout/fragment_overview.xml. Exclua todo o elemento <TextView>.
  2. Adicione este elemento <RecyclerView>, que usa um GridLayoutManager e o layout grid_view_item para um único item:
<androidx.recyclerview.widget.RecyclerView
            android:id="@+id/photos_grid"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:padding="6dp"
            android:clipToPadding="false"
            app:layoutManager=
               "androidx.recyclerview.widget.GridLayoutManager"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:spanCount="2"
            tools:itemCount="16"
            tools:listitem="@layout/grid_view_item" />

Etapa 3: Adicione o adaptador de grade de fotos

Agora, o layout fragment_overview tem um RecyclerView, enquanto o layout grid_view_item tem um único ImageView. Nesta etapa, você vincula os dados ao RecyclerView por meio de um adaptador RecyclerView.

  1. Abra overview/PhotoGridAdapter.kt.
  2. Crie a classe PhotoGridAdapter, com os parâmetros do construtor mostrados abaixo. A classe PhotoGridAdapter estende ListAdapter, cujo construtor precisa do tipo de item de lista, o recipiente de vistas e uma implementação DiffUtil.ItemCallback.

    Importe as classes androidx.recyclerview.widget.ListAdapter e com.example.android.marsrealestate.network.MarsProperty quando solicitado. Nas etapas a seguir, você implementa as outras partes ausentes desse construtor que estão produzindo erros.
class PhotoGridAdapter : ListAdapter<MarsProperty,
        PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {

}
  1. Clique em qualquer lugar na classe PhotoGridAdapter e pressione Control+i para implementar os métodos ListAdapter, que são onCreateViewHolder() e onBindViewHolder().
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoGridAdapter.MarsPropertyViewHolder {
   TODO("not implemented") 
}

override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPropertyViewHolder, position: Int) {
   TODO("not implemented") 
}
  1. No final da definição da classe PhotoGridAdapter, após os métodos que você acabou de adicionar, adicione uma definição de objeto complementar para DiffCallback, conforme mostrado abaixo.

    Importe androidx.recyclerview.widget.DiffUtil quando solicitado.

    O objeto DiffCallback estende DiffUtil.ItemCallback com o tipo de objeto que você deseja comparar—MarsProperty.
companion object DiffCallback : DiffUtil.ItemCallback<MarsProperty>() {
}
  1. Pressione Control+i para implementar os métodos comparadores para este objeto, que são areItemsTheSame() e areContentsTheSame().
override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
   TODO("not implemented") 
}

override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
   TODO("not implemented") }
  1. Para o método areItemsTheSame(), remova o TODO. Use o operador de igualdade referencial de Kotlin (===), que retorna true se o objeto fizer referência a oldItem e newItem são os mesmos.
override fun areItemsTheSame(oldItem: MarsProperty, 
                  newItem: MarsProperty): Boolean {
   return oldItem === newItem
}
  1. Para areContentsTheSame(), use o operador de igualdade padrão apenas no ID de oldItem e newItem.
override fun areContentsTheSame(oldItem: MarsProperty, 
                  newItem: MarsProperty): Boolean {
   return oldItem.id == newItem.id
}
  1. Ainda dentro da classe PhotoGridAdapter, abaixo do objeto complementar, adicione uma definição de classe interna para MarsPropertyViewHolder, que estende RecyclerView.ViewHolder.

    Importe androidx.recyclerview.widget.RecyclerView e com.example.android.marsrealestate.databinding.GridViewItemBinding quando solicitado.

    Você precisa do GridViewItemBinding variável para vincular a MarsProperty ao layout, então passe a variável para MarsPropertyViewHolder. Como a classe base ViewHolder requer uma vista em seu construtor, você passa a ela a vista raiz da vinculação.
class MarsPropertyViewHolder(private var binding: 
                   GridViewItemBinding):
       RecyclerView.ViewHolder(binding.root) {
}
  1. Em MarsPropertyViewHolder, crie um método bind() que recebe um objeto MarsProperty como argumento e define binding.property para esse objeto. Chame executePendingBindings() depois de definir a propriedade, o que faz com que a atualização seja executada imediatamente.
fun bind(marsProperty: MarsProperty) {
   binding.property = marsProperty
   binding.executePendingBindings()
}
  1. Em onCreateViewHolder(), remova o TODO e adicione a linha mostrada abaixo. Importe android.view.LayoutInflater quando solicitado.

    O método onCreateViewHolder() precisa retornar um novo MarsPropertyViewHolder, criado inflando o GridViewItemBinding e usando o LayoutInflater de seu contexto ViewGroup pai.
   return MarsPropertyViewHolder(GridViewItemBinding.inflate(
      LayoutInflater.from(parent.context)))
  1. No método onBindViewHolder(), remova o TODO e adicione as linhas mostradas abaixo. Aqui você chama getItem() para obter o objeto MarsProperty associado à posição RecyclerView atual e, em seguida, passa essa propriedade para o vínculo () método no MarsPropertyViewHolder.
val marsProperty = getItem(position)
holder.bind(marsProperty)

Etapa 4: Adicione o adaptador de encadernação e conecte as peças

Finalmente, use um BindingAdapter para inicializar o PhotoGridAdapter com a lista de objetos MarsProperty. Use um BindingAdapter para definir os dados RecyclerView faz com que a vinculação de dados observe automaticamente o LiveData para a lista de objetos MarsProperty. Em seguida, o adaptador de vinculação é chamado automaticamente quando a lista MarsProperty muda.

  1. Abra BindingAdapters.kt.
  2. No final do arquivo, adicione um método bindRecyclerView() que recebe um RecyclerView e uma lista de objetos MarsProperty como argumentos. Anote esse método com um @BindingAdapter.

    Importe androidx.recyclerview.widget.RecyclerView e com.example.android.marsrealestate.network.MarsProperty quando solicitado.
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, 
    data: List<MarsProperty>?) {
}
  1. Dentro da função bindRecyclerView(), converta recyclerView.adapter para PhotoGridAdapter e chame adapter.submitList() com os dados. Isso informa ao RecyclerView quando uma nova lista está disponível.

Importe com.example.android.marsrealestate.overview.PhotoGridAdapter quando solicitado.

val adapter = recyclerView.adapter as PhotoGridAdapter
adapter.submitList(data)
  1. Abra res/layout/fragment_overview.xml. Adicione o atributo app:listData ao elemento RecyclerView e defina-o como viewmodel.properties usando vinculação de dados.
app:listData="@{viewModel.properties}"
  1. Abra overview/OverviewFragment.kt. Em onCreateView(), logo antes da chamada para setHasOptionsMenu(), inicialize o adaptador RecyclerView em binding.photosGrid para um novo objeto PhotoGridAdapter.
binding.photosGrid.adapter = PhotoGridAdapter()
  1. Execute o aplicativo. Você deverá ver uma grade de imagens MarsProperty. Conforme você rola para ver novas imagens, o aplicativo mostra o ícone de progresso de carregamento antes de exibir a própria imagem. Se você ativar o modo avião, as imagens que ainda não foram carregadas aparecem como ícones de imagem quebrada.

O aplicativo MarsRealEstate exibe o ícone de imagem quebrada quando uma imagem não pode ser buscada. Mas quando não há rede, o aplicativo mostra uma tela em branco.

Esta não é uma ótima experiência do usuário. Nesta tarefa, você adiciona tratamento básico de erros, para dar ao usuário uma ideia melhor do que está acontecendo. Se a Internet não estiver disponível, o aplicativo mostrará o ícone de erro de conexão. Enquanto o aplicativo busca a lista MarsProperty, o aplicativo mostra a animação de carregamento.

Etapa 1: Adicione status ao modelo de vista

Para começar, você cria um LiveData no modelo de vistas para representar o status da solicitação da web. Existem três estados a serem considerados - carregamento, sucesso e falha. O estado de carregamento ocorre enquanto você espera por dados na chamada para await().

  1. Abra overview/OverviewViewModel.kt. Na parte superior do arquivo (após as importações, antes da definição da classe), adicione um enum para representar todos os status disponíveis:
enum class MarsApiStatus { LOADING, ERROR, DONE }
  1. Renomeie as definições de dados ativos internos e externos _response em toda a classe OverviewViewModel para _status. Como você adicionou suporte para as _properties LiveData anteriormente neste tutorial, a resposta completa do serviço da web não foi usada. Você precisa de um LiveData aqui para controlar o status atual, então você pode simplesmente renomear as variáveis ​​existentes.

Além disso, altere os tipos de String para MarsApiStatus.

private val _status = MutableLiveData<MarsApiStatus>()

val status: LiveData<MarsApiStatus>
   get() = _status
  1. Role para baixo até o método getMarsRealEstateProperties() e atualize _response para _status aqui também. Altere a string "Success" para o estado MarsApiStatus.DONE e a string "Failure" para MarsApiStatus.ERROR.
  2. Defina o status para MarsApiStatus.LOADING antes do bloco try {}. Este é o status inicial enquanto a co-rotina está em execução e você está esperando pelos dados. O bloco try / catch {} completo agora se parece com isto:
_status.value = MarsApiStatus.LOADING
try {
   _properties.value = MarsApi.retrofitService.getProperties()
   _status.value = MarsApiStatus.DONE
} catch (e: Exception) {
   _status.value = MarsApiStatus.ERROR
}
  1. Após o estado de erro no bloco catch {}, defina as _properties LiveData para uma lista vazia. Isso limpa o RecyclerView.
} catch (e: Exception) {
   _status.value = MarsApiStatus.ERROR
   _properties.value = ArrayList()
}

Etapa 2: Adicione um adaptador de vinculação para o status ImageView

Agora você tem um status no modelo de vista, mas é apenas um conjunto de estados. Como você faz com que ele apareça no próprio aplicativo? Nesta etapa, você usa um ImageView, conectado à vinculação de dados, para exibir ícones para os estados de carregamento e erro. Quando o aplicativo está no estado de carregamento ou em estado de erro, o ImageView deve estar visível. Quando o aplicativo é carregado, o ImageView deve estar invisível.

  1. Abra BindingAdapters.kt. Adicione um novo adaptador de vinculação chamado bindStatus() que leva um valor ImageView e MarsApiStatus como argumentos. Importe com.example.android.marsrealestate.overview.MarsApiStatus quando solicitado.
@BindingAdapter("marsApiStatus")
fun bindStatus(statusImageView: ImageView, 
          status: MarsApiStatus?) {
}
  1. Adicione um when {} dentro do método bindStatus() para alternar entre os diferentes status.
when (status) {

}
  1. Dentro de when {}, adicione um caso para o estado de carregamento (MarsApiStatus.LOADING). Para este estado, defina ImageView como visível e atribua a animação de carregamento. Este é o mesmo drawable de animação que você usou para o Glide na tarefa anterior. Importe android.view.View quando solicitado.
when (status) {
   MarsApiStatus.LOADING -> {
      statusImageView.visibility = View.VISIBLE
      statusImageView.setImageResource(R.drawable.loading_animation)
   }
}
  1. Adicione um caso para o estado de erro, que é MarsApiStatus.ERROR. Similarmente ao que você fez para o estado LOADING, defina o status ImageView para visível e reutilize o drawable de erro de conexão.
MarsApiStatus.ERROR -> {
   statusImageView.visibility = View.VISIBLE
   statusImageView.setImageResource(R.drawable.ic_connection_error)
}
  1. Adicione um caso para o estado concluído, que é MarsApiStatus.DONE. Aqui você obteve uma resposta bem-sucedida, então desligue a visibilidade do status ImageView para ocultá-lo.
MarsApiStatus.DONE -> {
   statusImageView.visibility = View.GONE
}

Etapa 3: Adicione o status ImageView ao layout

  1. Abra res/layout/fragment_overview.xml. Abaixo do elemento RecyclerView, dentro do ConstraintLayout, adicione o ImageView mostrado abaixo.

    Este ImageView tem as mesmas restrições do RecyclerView. No entanto, a largura e a altura usam wrap_content para centralizar a imagem em vez de esticar a imagem para preencher a vista. Observe também o atributo app:marsApiStatus, que faz com que a vista chame seu BindingAdapter quando a propriedade de status no modelo de vistas muda.
<ImageView
   android:id="@+id/status_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:marsApiStatus="@{viewModel.status}" />
  1. Ative o modo avião em seu emulador ou dispositivo para simular uma conexão de rede ausente. Compile e execute o aplicativo e observe que a imagem de erro aparece:

  1. Toque no botão Voltar para fechar o aplicativo e desligar o modo avião. Use a tela Recentes para retornar o aplicativo. Dependendo da velocidade de sua conexão de rede, você poderá ver um botão giratório de carregamento extremamente breve quando o aplicativo consultar o serviço da web antes que as imagens comecem a carregar.

Projeto Android Studio: MarsRealEstateGrid

Documentação para desenvolvimento em Android:

De outros:

Esta seção lista as possíveis tarefas de casa para os alunos que estão trabalhando neste tutorial como parte de um curso ministrado por um instrutor.

Responda estas perguntas

Pergunta 1

Qual método Glide você usa para indicar o ImageView que conterá a imagem carregada?

into()

with()

imageview()

apply()

Pergunta 2

Como você especifica um espaço reservado para imagem para mostrar quando o Glide está carregando?

▢ Use o método into() com um drawable.

▢ Use RequestOptions() e chame o método placeholder() com um drawable.

▢ Atribua a propriedade Glide.placeholder a um drawable.

▢ Use RequestOptions() e chame o método loadingImage() com um drawable.

Pergunta 3

Como você indica que um método é um adaptador de vinculação?

▢ Chame o método setBindingAdapter() no LiveData.

▢ Coloque o método em um arquivo Kotlin chamado BindingAdapters.kt.

▢ Use o atributo android:adapter no layout XML.

▢ Anote o método com @BindingAdapter.

Comece a próxima lição: 08.3: Filtragem e visualizações detalhadas com dados da Internet

Para obter enlaces para outros tutoriais neste curso, consulte a página de destino dos tutoriais Fundamentos de Android em Kotlin.