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
No codelab anterior, você aprendeu a receber dados de um serviço da Web e analisar a resposta em um objeto de dados. Neste codelab, você vai aproveitar esse conhecimento para carregar e mostrar fotos de um URL da Web. Também vamos lembrar como criar uma RecyclerView e usá-la para mostrar uma grade de imagens na página de visão geral.
O que você já precisa saber
- Como criar e usar fragmentos.
- Como usar componentes de arquitetura, incluindo ViewModels, fábricas de ViewModel, transformações e
LiveData. - Saber como extrair um arquivo JSON de um serviço REST da Web e analisar esses dados em objetos Kotlin usando as bibliotecas Retrofit e Moshi (links em inglês).
- Como criar um layout de grade usando um
RecyclerView. - Como
Adapter,ViewHoldereDiffUtilfuncionam.
O que você vai aprender
- Como usar a biblioteca Glide para carregar e mostrar uma imagem de um URL da Web.
- Como usar um
RecyclerViewe um adaptador de grade para exibir uma grade de imagens. - Como processar erros possíveis durante o download e a exibição das imagens.
O que você aprenderá
- Modificar o app MarsRealEstate para acessar o URL dos dados de propriedades de Marte e usar o Glide para carregar e mostrar essas imagens.
- Adicionar uma animação e um ícone de erro de carregamento ao app.
- Usará um
RecyclerViewpara mostrar uma grade de imagens de propriedades de Marte. - Adicionará o status e o processamento de erros ao
RecyclerView.
Neste codelab (e nos relacionados), você vai trabalhar com um app chamado MarsRealEstate, que mostra propriedades à venda em Marte. O app se conecta a um servidor da Internet para recuperar e mostrar dados de propriedades, incluindo detalhes como preço e disponibilidade para venda ou aluguel. As imagens que representam cada propriedade são fotos reais de Marte capturadas por rovers da NASA.

A versão do app que você vai criar neste codelab preenche a página de visão geral, que mostra uma grade de imagens. As imagens fazem parte dos dados de propriedade que o app recebe do serviço da Web de imóveis em Marte. Seu app vai usar a biblioteca Glide para carregar e mostrar as imagens, e um RecyclerView para criar o layout de grade para elas. O app também processará os erros de rede corretamente.
Mostrar uma foto de um URL da Web pode parecer simples, mas requer muito trabalho técnico para que isso funcione bem. A imagem precisa ser transferida por download, armazenada em buffer e decodificada do formato compactado para uma imagem que o Android possa usar. A imagem precisa ser armazenada em cache na memória, no cache baseado em armazenamento ou em ambos. Tudo isso precisa acontecer em segmentos de baixa prioridade em segundo plano para que a IU permaneça responsiva. Além disso, para melhor o desempenho de rede e CPU, convém buscar e decodificar mais de uma imagem ao mesmo tempo. Aprender a carregar imagens da rede de forma eficaz pode ser um codelab por si só.
Felizmente, é possível usar uma biblioteca criada pela comunidade, a Glide (link em inglês), para fazer o download, armazenar em um buffer, decodificar e armazenar as imagens em cache. O Glide deixa você com muito menos trabalho do que se tivesse que fazer tudo isso do zero.
Em resumo, a Glide precisa de duas coisas:
- O URL da imagem que você quer carregar e mostrar.
- Um objeto
ImageViewpara mostrar essa imagem.
Nesta tarefa, você vai aprender a usar o Glide para mostrar uma única imagem do web service imobiliário. Você vai mostrar a imagem que representa o primeiro terreno em Marte na lista de propriedades que o serviço da Web retorna. Veja as capturas de tela antes e depois:


Etapa 1: adicionar a dependência do Glide
- Abra o app MarsRealEstate do último codelab. (Faça o download do MarsRealEstateNetwork aqui se você não tiver o app.)
- Execute o app para ver o que ele faz. (Ele mostra detalhes de texto de uma propriedade que está hipoteticamente disponível em Marte.)
- Abra o arquivo build.gradle (Module: app).
- Na seção
dependencies, adicione esta linha para a biblioteca Glide:
implementation "com.github.bumptech.glide:glide:$version_glide"
O número da versão já está definido separadamente no arquivo Gradle do projeto.
- Clique em Sync Now para recriar o projeto com a nova dependência.
Etapa 2: atualizar o modelo de visualização
Em seguida, atualize a classe OverviewViewModel para incluir dados em tempo real de uma única propriedade de Marte.
- Abra
overview/OverviewViewModel.kt. Logo abaixo doLiveDatapara o_response, adicione dados dinâmicos internos (mutáveis) e externos (imutáveis) para um único objetoMarsProperty.
Importe a classeMarsProperty(com.example.android.marsrealestate.network.MarsProperty) quando solicitado.
private val _property = MutableLiveData<MarsProperty>()
val property: LiveData<MarsProperty>
get() = _property- No método
getMarsRealEstateProperties(), encontre a linha no blocotry/catch {}que define_response.valuecomo o número de propriedades. Adicione o teste mostrado abaixo. Se os objetosMarsPropertyestiverem disponíveis, esse teste vai definir o valor de_propertyLiveDatacomo a primeira propriedade emlistResult.
if (listResult.size > 0) {
_property.value = listResult[0]
}O bloco try/catch {} completo agora ficará assim:
try {
var listResult = getPropertiesDeferred.await()
_response.value = "Success: ${listResult.size} Mars properties retrieved"
if (listResult.size > 0) {
_property.value = listResult[0]
}
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}- Abra o arquivo
res/layout/fragment_overview.xml. No elemento<TextView>, mudeandroid:textpara vincular ao componenteimgSrcUrldopropertyLiveData:
android:text="@{viewModel.property.imgSrcUrl}"- Execute o app. O
TextViewmostra apenas o URL da imagem na primeira propriedade de Marte. Você já configurou o ViewModel e o LiveData para o URL.

Etapa 3: criar um adaptador de vinculação e chamar o Glide
Agora você tem o URL de uma imagem para mostrar, e é hora de começar a trabalhar com o Glide para carregar essa imagem. Nesta etapa, você vai usar um adaptador de vinculação para extrair o URL de um atributo XML associado a uma ImageView e usar o Glide para carregar a imagem. Os adaptadores de vinculação são métodos de extensão que ficam entre uma visualização e os dados vinculados para fornecer um comportamento personalizado quando os dados mudam. Nesse caso, o comportamento personalizado é chamar o Glide para carregar uma imagem de um URL em uma ImageView.
- Abra
BindingAdapters.kt. Esse arquivo conterá os adaptadores de vinculação que você usa em todo o app. - Crie uma função
bindImage()que receba umaImageViewe umaStringcomo parâmetros. Adicione a anotação@BindingAdapterà função. A anotação@BindingAdapterinforma à vinculação de dados que você quer que esse adaptador de vinculação seja executado quando um item XML tiver o atributoimageUrl.
Importeandroidx.databinding.BindingAdaptereandroid.widget.ImageViewquando solicitado.
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
}- Na função
bindImage(), adicione um blocolet {}ao argumentoimgUrl:
imgUrl?.let {
}- No bloco
let {}, adicione a linha mostrada abaixo para converter a string de URL (do XML) em um objetoUri. Importeandroidx.core.net.toUriquando solicitado.
Você quer que o objetoUrifinal use o esquema HTTPS, porque o servidor de onde você extrai as imagens exige esse esquema. Para usar o esquema HTTPS, anexebuildUpon.scheme("https")ao buildertoUri. O métodotoUri()é uma função de extensão Kotlin da biblioteca principal do Android KTX. Por isso, parece que ele faz parte da classeString.
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()- Ainda no
let {}, chameGlide.with()para carregar a imagem do objetoUrinoImageView. Importecom.bumptech.glide.Glidequando solicitado.
Glide.with(imgView.context)
.load(imgUri)
.into(imgView)Etapa 4: atualizar o layout e os fragmentos
Embora o Glide tenha carregado a imagem, ainda não há nada para ver. A próxima etapa é atualizar o layout e os fragmentos com um ImageView para mostrar a imagem.
- Abra
res/layout/gridview_item.xml. Esse é o arquivo de recursos de layout que você vai usar para cada item noRecyclerViewmais adiante no codelab. Você o usa temporariamente aqui para mostrar apenas a imagem única. - Acima do elemento
<ImageView>, adicione um elemento<data>para a vinculação de dados e vincule-o à classeOverviewViewModel:
<data>
<variable
name="viewModel"
type="com.example.android.marsrealestate.overview.OverviewViewModel" />
</data>- Adicione um atributo
app:imageUrlao elementoImageViewpara usar o novo adaptador de vinculação de carregamento de imagem:
app:imageUrl="@{viewModel.property.imgSrcUrl}"- Abra
overview/OverviewFragment.kt. No métodoonCreateView(), use um comentário para excluir a linha que infla a classeFragmentOverviewBindinge a atribui à variável de vinculação. Isso é temporário. Você vai voltar a ele mais tarde.
//val binding = FragmentOverviewBinding.inflate(inflater)- Adicione uma linha para inflar a classe
GridViewItemBinding. Importecom.example.android.marsrealestate. databinding.GridViewItemBindingquando solicitado.
val binding = GridViewItemBinding.inflate(inflater)- Execute o app. Agora você vai ver a foto da imagem do primeiro
MarsPropertyna lista de resultados.
Etapa 5: adicionar imagens simples de carregamento e erro
O Glide pode melhorar a experiência do usuário mostrando uma imagem de marcador enquanto a imagem real é carregada, ou exibir uma imagem de erro se o carregamento falhar, por exemplo, se a imagem não existir ou estiver corrompida. Nesta etapa, você vai adicionar essa funcionalidade ao adaptador de vinculação e ao layout.
- Abra
res/drawable/ic_broken_image.xmle clique na guia Visualizar à direita. Para a imagem de erro, você usa o ícone de imagem corrompida que está disponível na biblioteca de ícones integrada. Esse drawable vetorial usa o atributoandroid:tintpara colorir o ícone como cinza.

- Abra
res/drawable/loading_animation.xml. Esse drawable é uma animação definida com a tag<animate-rotate>. A animação gira um drawable de imagem,loading_img.xml, ao redor do ponto central. Essa animação não vai ser mostrada na visualização.

- Retorne ao arquivo
BindingAdapters.kt. No métodobindImage(), atualize a chamada paraGlide.with()e chame a funçãoapply()entreload()einto(). Importecom.bumptech.glide.request.RequestOptionsquando solicitado.
Esse código define a imagem de carregamento de marcador a ser usada durante o carregamento (o drawableloading_animation). O código também define uma imagem que será usada se o carregamento da imagem falhar (o drawablebroken_image). O métodobindImage()completo agora ficará assim:
@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)
}
}
- Execute o app. Dependendo da velocidade da sua conexão de rede, você poderá ver brevemente o carregamento da imagem, à medida que o Glide transfere a imagem da propriedade e a exibe. No entanto, o ícone de imagem corrompida ainda não será mostrado, mesmo que você desative a rede. Isso será corrigido na última parte do codelab.
Agora seu app carrega informações de propriedades da Internet. Usando dados do primeiro item da lista MarsProperty, você criou uma propriedade LiveData no modelo de visualização e usou o URL da imagem desses dados de propriedade para preencher uma ImageView. Mas o objetivo é que o app mostre uma grade de imagens, então você vai usar um RecyclerView com um GridLayoutManager.
Etapa 1: atualizar o modelo de visualização
No momento, o modelo de visualização tem um _property LiveData que contém um objeto MarsProperty, o primeiro da lista de respostas do serviço da Web. Nesta etapa, você muda esse LiveData para armazenar a lista completa de objetos MarsProperty.
- Abra
overview/OverviewViewModel.kt. - Mude a variável privada
_propertypara_properties. Mude o tipo para ser uma lista de objetosMarsProperty.
private val _properties = MutableLiveData<List<MarsProperty>>()- Substitua os dados em tempo real externos
propertyporproperties. Adicione a lista ao tipoLiveDataaqui também:
val properties: LiveData<List<MarsProperty>>
get() = _properties- Role para baixo até encontrar o método
getMarsRealEstateProperties(). Dentro do blocotry {}, substitua todo o teste que você adicionou na tarefa anterior pela linha mostrada abaixo. Como a variávellistResultcontém uma lista de objetosMarsProperty, basta atribuí-la a_properties.valueem vez de testar uma resposta bem-sucedida.
_properties.value = listResultO bloco try/catch como um todo ficará assim:
try {
var listResult = getPropertiesDeferred.await()
_response.value = "Success: ${listResult.size} Mars properties retrieved"
_properties.value = listResult
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}Etapa 2: atualizar os layouts e fragmentos
A próxima etapa é mudar o layout e os fragmentos do app para usar uma visualização de reciclagem e um layout de grade, em vez de uma única visualização de imagem.
- Abra
res/layout/gridview_item.xml. Mude a vinculação de dados deOverviewViewModelparaMarsPropertye renomeie a variável como"property".
<variable
name="property"
type="com.example.android.marsrealestate.network.MarsProperty" />- Na
<ImageView>, mude o atributoapp:imageUrlpara se referir ao URL da imagem no objetoMarsProperty:
app:imageUrl="@{property.imgSrcUrl}"- Abra
overview/OverviewFragment.kt. EmonCreateview(), remova a marca de comentário da linha que inflaFragmentOverviewBinding. Exclua ou comente a linha que infla aGridViewBinding. Essas mudanças desfazem as temporárias feitas na tarefa anterior.
val binding = FragmentOverviewBinding.inflate(inflater)
// val binding = GridViewItemBinding.inflate(inflater)- Abra
res/layout/fragment_overview.xml. Exclua todo o elemento<TextView>. - Em vez disso, adicione este elemento
<RecyclerView>, que usa umGridLayoutManagere o layoutgrid_view_itempara 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: adicionar 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ê vai vincular os dados à RecyclerView usando um adaptador da RecyclerView.
- Abra
overview/PhotoGridAdapter.kt. - Crie a classe
PhotoGridAdaptercom os parâmetros do construtor mostrados abaixo. A classePhotoGridAdapterestende oListAdapter, cujo construtor exige o tipo de item de lista, o armazenador de visualização e uma implementação deDiffUtil.ItemCallback.
Importe as classesandroidx.recyclerview.widget.ListAdapterecom.example.android.marsrealestate.network.MarsPropertyquando solicitado. Nas etapas a seguir, você vai implementar as outras partes ausentes desse construtor que estão produzindo erros.
class PhotoGridAdapter : ListAdapter<MarsProperty,
PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
}- Clique em qualquer lugar na classe
PhotoGridAdaptere pressioneControl+ipara implementar os métodosListAdapter, que sãoonCreateViewHolder()eonBindViewHolder().
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoGridAdapter.MarsPropertyViewHolder {
TODO("not implemented")
}
override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPropertyViewHolder, position: Int) {
TODO("not implemented")
}- No final da definição da classe
PhotoGridAdapter, depois dos métodos que você acabou de adicionar, adicione uma definição de objeto complementar paraDiffCallback, conforme mostrado abaixo.
Importeandroidx.recyclerview.widget.DiffUtilquando solicitado.
O objetoDiffCallbackestende oDiffUtil.ItemCallbackcom o tipo de objeto que você quer comparar,MarsProperty.
companion object DiffCallback : DiffUtil.ItemCallback<MarsProperty>() {
}- Pressione
Control+ipara implementar os métodos do comparador para esse objeto, que sãoareItemsTheSame()eareContentsTheSame().
override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
TODO("not implemented")
}
override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
TODO("not implemented") }- No método
areItemsTheSame(), remova o TODO. Use o operador de igualdade referencial do Kotlin (===), que retornatruese as referências de objeto paraoldItemenewItemforem as mesmas.
override fun areItemsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem === newItem
}- Para
areContentsTheSame(), use o operador de igualdade padrão apenas no ID deoldItemenewItem.
override fun areContentsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem.id == newItem.id
}- Ainda dentro da classe
PhotoGridAdapter, abaixo do objeto complementar, adicione uma definição de classe interna paraMarsPropertyViewHolder, que estendeRecyclerView.ViewHolder.
Importeandroidx.recyclerview.widget.RecyclerViewecom.example.android.marsrealestate.databinding.GridViewItemBindingquando solicitado.
Você precisa da variávelGridViewItemBindingpara vincularMarsPropertyao layout. Portanto, transmita a variável paraMarsPropertyViewHolder. Como a classe de baseViewHolderrequer uma visualização no construtor, transmita a visualização raiz de vinculação a ela.
class MarsPropertyViewHolder(private var binding:
GridViewItemBinding):
RecyclerView.ViewHolder(binding.root) {
}- No
MarsPropertyViewHolder, crie um métodobind()que recebe um objetoMarsPropertycomo argumento e define abinding.propertypara esse objeto. ChameexecutePendingBindings()depois de definir a propriedade, o que fará com que a atualização seja executada imediatamente.
fun bind(marsProperty: MarsProperty) {
binding.property = marsProperty
binding.executePendingBindings()
}- Em
onCreateViewHolder(), remova o TODO e adicione a linha mostrada abaixo. Importeandroid.view.LayoutInflaterquando solicitado.
O métodoonCreateViewHolder()precisa retornar um novoMarsPropertyViewHolder, criado inflando aGridViewItemBindinge usando oLayoutInflaterdo contextoViewGrouppai.
return MarsPropertyViewHolder(GridViewItemBinding.inflate(
LayoutInflater.from(parent.context)))- No método
onBindViewHolder(), remova o código marcado como "TODO" e adicione as linhas mostradas abaixo. Aqui, você chamagetItem()para receber o objetoMarsPropertyassociado à posição atual doRecyclerViewe, em seguida, transmite essa propriedade para o métodobind()noMarsPropertyViewHolder.
val marsProperty = getItem(position)
holder.bind(marsProperty)Etapa 4: adicionar o adaptador de vinculação e conectar as partes
Por fim, use um BindingAdapter para inicializar o PhotoGridAdapter com a lista de objetos MarsProperty. Usar um BindingAdapter para definir os dados do RecyclerView faz com que a vinculação de dados observe automaticamente o LiveData da lista de objetos MarsProperty. O adaptador de vinculação será chamado automaticamente quando a lista do MarsProperty mudar.
- Abra o arquivo
BindingAdapters.kt. - No final do arquivo, adicione um método
bindRecyclerView()que usa umRecyclerViewe uma lista de objetosMarsPropertycomo argumentos. Anote esse método com um@BindingAdapter.
Importeandroidx.recyclerview.widget.RecyclerViewecom.example.android.marsrealestate.network.MarsPropertyquando solicitado.
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView,
data: List<MarsProperty>?) {
}- Na função
bindRecyclerView(), transmitarecyclerView.adapterparaPhotoGridAdaptere chameadapter.submitList()com os dados. Isso informa oRecyclerViewquando uma nova lista está disponível.
Importe com.example.android.marsrealestate.overview.PhotoGridAdapter quando solicitado.
val adapter = recyclerView.adapter as PhotoGridAdapter
adapter.submitList(data)- Abra
res/layout/fragment_overview.xml. Adicione o atributoapp:listDataao elementoRecyclerViewe defina-o comoviewmodel.propertiesusando a vinculação de dados.
app:listData="@{viewModel.properties}"- Abra
overview/OverviewFragment.kt. EmonCreateView(), logo antes da chamada parasetHasOptionsMenu(), inicialize o adaptadorRecyclerViewembinding.photosGridpara um novo objetoPhotoGridAdapter.
binding.photosGrid.adapter = PhotoGridAdapter()- Execute o app. Você vai ver uma grade de imagens de
MarsProperty. Ao rolar para ver novas imagens, o app mostra o ícone de progresso do carregamento antes de exibir a imagem em si. Se você ativar o modo avião, as imagens que ainda não foram carregadas vão aparecer como ícones de imagem corrompida.

O app MarsRealEstate mostra o ícone de imagem corrompida quando uma imagem não pode ser buscada. No entanto, quando não há rede, o app mostra uma tela em branco.

Essa não é uma ótima experiência do usuário. Nesta tarefa, você adicionará um processamento 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 app vai mostrar o ícone de erro de conexão. Enquanto o app busca a lista de MarsProperty, uma animação de carregamento é exibida.
Etapa 1: adicionar status ao modelo de visualização
Para começar, crie um LiveData no modelo de visualização para representar o status da solicitação da Web. Há três estados a serem considerados: carregamento, sucesso e falha. O estado de carregamento acontece enquanto você aguarda os dados na chamada para await().
- Abra
overview/OverviewViewModel.kt. Na parte superior do arquivo (após as importações e antes da definição das classes), adicione umaenumpara representar todos os status disponíveis:
enum class MarsApiStatus { LOADING, ERROR, DONE }- Renomeie as definições de dados ativos
_responseinternas e externas em toda a classeOverviewViewModelpara_status. Como você adicionou suporte para o_propertiesLiveDatano início deste codelab, a resposta completa do serviço da Web não foi usada. Você precisa de umLiveDataaqui para acompanhar o status atual. Basta renomear as variáveis atuais.
Além disso, mude os tipos de String para MarsApiStatus..
private val _status = MutableLiveData<MarsApiStatus>()
val status: LiveData<MarsApiStatus>
get() = _status- Role para baixo até o método
getMarsRealEstateProperties()e atualize_responsepara_statusaqui também. Mude a string"Success"para o estadoMarsApiStatus.DONEe a string"Failure"paraMarsApiStatus.ERROR. - Adicione um status
MarsApiStatus.LOADINGà parte de cima do blocotry {}, antes da chamada paraawait(). Esse será o status inicial enquanto a corrotina está em execução e você está aguardando os dados. O blocotry/catch {}completo agora ficará assim:
try {
_status.value = MarsApiStatus.LOADING
var listResult = getPropertiesDeferred.await()
_status.value = MarsApiStatus.DONE
_properties.value = listResult
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
}- Depois do estado de erro no bloco
catch {}, defina_propertiesLiveDatacomo uma lista vazia. Isso limpa oRecyclerView.
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
_properties.value = ArrayList()
}Etapa 2: adicionar um adaptador de vinculação ao status da ImageView
Agora você tem um status no modelo de visualização, mas ele é apenas um conjunto de estados. Como fazer com que ele apareça no próprio app? Nesta etapa, você usa uma ImageView, conectada à vinculação de dados, para mostrar ícones dos estados de carregamento e erro. Quando o app estiver no estado de carregamento ou no estado de erro, a ImageView precisará estar visível. Quando o app terminar de carregar, a ImageView ficará invisível.
- Abra
BindingAdapters.kt. Adicione um novo adaptador de vinculação com o nomebindStatus()que usa os valoresImageVieweMarsApiStatuscomo argumentos. Importecom.example.android.marsrealestate.overview.MarsApiStatusquando solicitado.
@BindingAdapter("marsApiStatus")
fun bindStatus(statusImageView: ImageView,
status: MarsApiStatus?) {
}- Adicione um
when {}ao métodobindStatus()para alternar entre os diferentes status.
when (status) {
}- No método
when {}, adicione um caso para o estado de carregamento (MarsApiStatus.LOADING). Para esse estado, defina aImageViewcomo visível e atribua a animação de carregamento. Esse é o mesmo drawable de animação que você usou para o Glide na tarefa anterior. Importeandroid.view.Viewquando solicitado.
when (status) {
MarsApiStatus.LOADING -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.loading_animation)
}
}- Adiciona um caso para o estado de erro, que é
MarsApiStatus.ERROR. Da mesma forma que você fez para o estadoLOADING, defina o status daImageViewcomo visível e reutilize o drawable de erro de conexão.
MarsApiStatus.ERROR -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.ic_connection_error)
}- Adicione um caso para o estado concluído, que é
MarsApiStatus.DONE. Quando você receber uma resposta bem-sucedida, desative a visibilidade do status daImageViewpara ocultá-la.
MarsApiStatus.DONE -> {
statusImageView.visibility = View.GONE
}Etapa 3: adicionar o status da ImageView ao layout
- Abra
res/layout/fragment_overview.xml. Abaixo do elementoRecyclerView, dentro doConstraintLayout, adicione aImageViewmostrada abaixo.
EssaImageViewtem as mesmas restrições que oRecyclerView. No entanto, a largura e a altura usamwrap_contentpara centralizar a imagem em vez de esticá-la para preencher a visualização. O atributoapp:marsApiStatus, que faz com que a visualização chame oBindingAdapterquando a propriedade de status no modelo de visualização 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}" />- Ative o modo avião no emulador ou dispositivo para simular uma conexão de rede ausente. Compile e execute o app. Observe a imagem de erro exibida:

- Toque no botão "Voltar" para fechar o app e desativar o modo avião. Use a tela Recentes para retornar ao app. Dependendo da velocidade da conexão de rede, será possível ver um ícone de carregamento rapidamente quando o app consultar o serviço da Web antes que as imagens comecem a carregar.
Projeto do Android Studio: MarsRealEstateGrid (link em inglês)
- Para simplificar o processo de gerenciamento de imagens, use a biblioteca Glide para fazer o download, armazenar em buffer, decodificar e armazenar em cache imagens no seu app.
- O Glide precisa de duas coisas para carregar uma imagem da Internet: o URL de uma imagem e um objeto
ImageViewpara colocar a imagem. Para especificar essas opções, use os métodosload()einto()com o Glide. - Adaptadores de vinculação são métodos de extensão que ficam entre uma visualização e os dados vinculados dela. Adaptadores de vinculação oferecem comportamento personalizado quando os dados mudam, por exemplo, para chamar o Glide para carregar uma imagem de um URL em uma
ImageView. - Adaptadores de vinculação são métodos de extensão anotados com
@BindingAdapter. - Para adicionar opções à solicitação do Glide, use o método
apply(). Por exemplo, useapply()complaceholder()para especificar um elemento combinável de carregamento eapply()comerror()para especificar um elemento combinável de erro. - Para produzir uma grade de imagens, use um
RecyclerViewcom umGridLayoutManager. - Para atualizar a lista de propriedades quando ela mudar, use um adaptador de vinculação entre o
RecyclerViewe o layout.
Curso da Udacity:
Documentação do desenvolvedor Android:
- Visão geral do ViewModel
- Visão geral do LiveData
- Documentação oficial de corrotinas (link em inglês)
- Como vincular adaptadores
Outro:
- Glide (link em inglês)
- Como usar o Glide no Kotlin (link em inglês)
- Adaptadores de vinculação no Kotlin (link em inglês)
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
Qual método do Glide usar para indicar a ImageView que vai conter a imagem carregada?
▢ into()
▢ with()
▢ imageview()
▢ apply()
Pergunta 2
Como especificar uma imagem de marcador para ser exibida quando o Glide estiver sendo carregado?
▢ 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 indicar 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.
▢ Adicione a anotação @BindingAdapter ao método.
Comece a próxima lição:
Para acessar links de outros codelabs neste curso, consulte a página inicial dos codelabs de conceitos básicos do Kotlin para Android.