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
Nos codelabs anteriores desta lição, você aprendeu a extrair dados sobre imóveis em Marte de um serviço da Web e a criar um RecyclerView com um layout de grade para carregar e mostrar imagens desses dados. Neste codelab, você vai concluir o app MarsRealEstate implementando a capacidade de filtrar os terrenos em Marte de acordo com a disponibilidade para aluguel ou compra. Você também cria uma visualização detalhada para que, se o usuário tocar em uma foto de propriedade na visão geral, ele veja uma visualização detalhada com informações sobre essa propriedade.
O que você já precisa saber
- Como criar e usar fragmentos.
- Como navegar entre fragmentos e usar o Safe Args (um plug-in do Gradle) para transmitir dados entre eles.
- Como usar componentes de arquitetura, incluindo ViewModels, fábricas de ViewModel, transformações e
LiveData. - Como extrair dados codificados em JSON de um serviço REST da Web e analisar esses dados em objetos Kotlin com as bibliotecas Retrofit e Moshi (links em inglês).
O que você vai aprender
- Como usar expressões de vinculação complexas nos arquivos de layout.
- Como fazer solicitações da Retrofit a um serviço da Web com opções de consulta.
O que você aprenderá
- Modifique o app MarsRealEstate para marcar os terrenos em Marte que estão à venda (em vez dos que estão para aluguel) com um ícone de cifrão.
- Use o menu de opções na página de visão geral para criar uma solicitação de serviço da Web que filtre os terrenos em Marte por tipo.
- Crie um fragmento de detalhes para uma propriedade de Marte, conecte esse fragmento à grade de visão geral com navegação e transmita os dados da propriedade para esse fragmento.
Neste codelab (e nos relacionados), você vai trabalhar com um app chamado MarsRealEstate, que mostra propriedades à venda em Marte. Esse 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. Em codelabs anteriores, você criou um RecyclerView com um layout de grade para todas as fotos de propriedades:

Nesta versão do app, você vai trabalhar com o tipo da propriedade (aluguel ou compra) e adicionar um ícone ao layout de grade para marcar as propriedades à venda:

Modifique o menu de opções do app para filtrar a grade e mostrar apenas as propriedades para aluguel ou venda:

Por fim, você vai criar uma visualização detalhada para um terreno e conectar os ícones na grade de visão geral a esse fragmento de detalhes com navegação:

Até agora, a única parte dos dados da propriedade em Marte que você usou foi o URL da imagem da propriedade. Mas os dados da propriedade, que você definiu na classe MarsProperty, também incluem um ID, um preço e um tipo (aluguel ou venda). Para relembrar, aqui está um snippet dos dados JSON que você recebe do serviço da Web:
{
"price":8000000,
"id":"424908",
"type":"rent",
"img_src": "http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631290305226E03_DXXX.jpg"
},Nesta tarefa, você vai começar a trabalhar com o tipo de propriedade de Marte para adicionar uma imagem de cifrão às propriedades na página de visão geral que estão à venda.
Etapa 1: atualizar MarsProperty para incluir o tipo
A classe MarsProperty define a estrutura de dados de cada propriedade fornecida pelo serviço da Web. Em um codelab anterior, você usou a biblioteca Moshi para analisar a resposta JSON bruta do serviço da Web de Marte em objetos de dados MarsProperty individuais.
Nesta etapa, você adiciona uma lógica à classe MarsProperty para indicar se uma propriedade está para aluguel ou não (ou seja, se o tipo é a string "rent" ou "buy"). Você vai usar essa lógica em mais de um lugar. Por isso, é melhor tê-la aqui na classe de dados do que replicá-la.
- Abra o app MarsRealEstate do último codelab. (Faça o download do MarsRealEstateGrid se você não tiver o app.)
- Abra
network/MarsProperty.kt. Adicione um corpo à definição da classeMarsPropertye um getter personalizado paraisRentalque retornetruese o objeto for do tipo"rent".
data class MarsProperty(
val id: String,
@Json(name = "img_src") val imgSrcUrl: String,
val type: String,
val price: Double) {
val isRental
get() = type == "rent"
}Etapa 2: atualizar o layout do item de grade
Agora, atualize o layout do item para a grade de imagens e mostre um ícone de cifrão apenas nas imagens de propriedades à venda:

Com as expressões de vinculação de dados, é possível fazer esse teste totalmente no layout XML dos itens da grade.
- Abra
res/layout/grid_view_item.xml. Esse é o arquivo de layout de cada célula individual no layout de grade doRecyclerView. No momento, o arquivo contém apenas o elemento<ImageView>para a imagem da propriedade. - Dentro do elemento
<data>, adicione um elemento<import>para a classeView. Você usa importações quando quer usar componentes de uma classe em uma expressão de vinculação de dados em um arquivo de layout. Nesse caso, você vai usar as constantesView.GONEeView.VISIBLE. Portanto, é necessário ter acesso à classeView.
<import type="android.view.View"/>- Envolva toda a visualização de imagem com um
FrameLayoutpara permitir que o elemento drawable de cifrão seja empilhado em cima da imagem da propriedade.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="170dp">
<ImageView
android:id="@+id/mars_image"
...
</FrameLayout>- Para o
ImageView, mude o atributoandroid:layout_heightparamatch_parente preencha o novo paiFrameLayout.
android:layout_height="match_parent"- Adicione um segundo elemento
<ImageView>logo abaixo do primeiro, dentro doFrameLayout. Use a definição mostrada abaixo. Essa imagem aparece no canto inferior direito do item da grade, em cima da imagem de Marte, e usa o elemento drawable definido emres/drawable/ic_for_sale_outline.xmlpara o ícone de cifrão.
<ImageView
android:id="@+id/mars_property_type"
android:layout_width="wrap_content"
android:layout_height="45dp"
android:layout_gravity="bottom|end"
android:adjustViewBounds="true"
android:padding="5dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_for_sale_outline"
tools:src="@drawable/ic_for_sale_outline"/>- Adicione o atributo
android:visibilityà visualização de imagemmars_property_type. Use uma expressão de vinculação para testar o tipo de propriedade e atribua a visibilidade aView.GONE(para uma locação) ouView.VISIBLE(para uma compra).
android:visibility="@{property.rental ? View.GONE : View.VISIBLE}"Até agora, você só viu expressões de vinculação em layouts que usam variáveis individuais definidas no elemento <data>. As expressões de vinculação são extremamente poderosas e permitem realizar operações como testes e cálculos matemáticos totalmente no layout XML. Nesse caso, você usa o operador ternário (?:) para realizar um teste (este objeto é uma locação?). Você fornece um resultado para verdadeiro (ocultar o ícone de cifrão com View.GONE) e outro para falso (mostrar esse ícone com View.VISIBLE).
O novo arquivo grid_view_item.xml completo é mostrado abaixo:
<layout 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">
<data>
<import type="android.view.View"/>
<variable
name="property"
type="com.example.android.marsrealestate.network.MarsProperty" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="170dp">
<ImageView
android:id="@+id/mars_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
android:padding="2dp"
app:imageUrl="@{property.imgSrcUrl}"
tools:src="@tools:sample/backgrounds/scenic"/>
<ImageView
android:id="@+id/mars_property_type"
android:layout_width="wrap_content"
android:layout_height="45dp"
android:layout_gravity="bottom|end"
android:adjustViewBounds="true"
android:padding="5dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_for_sale_outline"
android:visibility="@{property.rental ? View.GONE : View.VISIBLE}"
tools:src="@drawable/ic_for_sale_outline"/>
</FrameLayout>
</layout>- Compile e execute o app. Observe que as propriedades que não são aluguéis têm o ícone de cifrão.

No momento, o app mostra todos os terrenos em Marte na grade de visão geral. Se um usuário estivesse procurando um imóvel para alugar em Marte, seria útil ter os ícones para indicar quais das propriedades disponíveis estão à venda, mas ainda há muitas propriedades para rolar na página. Nesta tarefa, você vai adicionar um menu de opções ao fragmento de visão geral que permite ao usuário mostrar apenas imóveis para aluguel, apenas para venda ou todos.

Uma maneira de fazer isso é testar o tipo de cada MarsProperty na grade de visão geral e mostrar apenas as propriedades correspondentes. No entanto, o serviço da Web de Marte tem um parâmetro ou opção de consulta (chamado filter) que permite receber apenas propriedades do tipo rent ou buy. Você pode usar essa consulta de filtro com o URL do serviço da Web realestate em um navegador assim:
https://android-kotlin-fun-mars-server.appspot.com/realestate?filter=buyNesta tarefa, você vai modificar a classe MarsApiService para adicionar uma opção de consulta à solicitação de serviço da Web com a Retrofit. Em seguida, conecte o menu de opções para baixar novamente todos os dados de terrenos em Marte usando essa opção de consulta. Como a resposta que você recebe do serviço da Web contém apenas as propriedades de seu interesse, não é necessário mudar a lógica de exibição da visualização para a grade de visão geral.
Etapa 1: atualizar o serviço da API Mars
Para mudar a solicitação, você precisa acessar novamente a classe MarsApiService que implementou no primeiro codelab desta série. Você modifica a classe para fornecer uma API de filtragem.
- Abra
network/MarsApiService.kt. Logo abaixo das importações, crie umenumchamadoMarsApiFilterpara definir constantes que correspondam aos valores de consulta esperados pelo serviço da Web.
enum class MarsApiFilter(val value: String) {
SHOW_RENT("rent"),
SHOW_BUY("buy"),
SHOW_ALL("all") }- Modifique o método
getProperties()para receber uma entrada de string para a consulta de filtro e anote essa entrada com@Query("filter"), conforme mostrado abaixo.
Importeretrofit2.http.Queryquando solicitado.
A anotação@Queryinforma ao métodogetProperties()(e, portanto, à Retrofit) para fazer a solicitação de serviço da Web com a opção de filtro. Cada vez quegetProperties()é chamado, o URL da solicitação inclui a parte?filter=type, que direciona o serviço da Web a responder com resultados que correspondem a essa consulta.
fun getProperties(@Query("filter") type: String): Etapa 2: atualizar o modelo de visualização geral
Você solicita dados do MarsApiService no método getMarsRealEstateProperties() em OverviewViewModel. Agora você precisa atualizar essa solicitação para receber o argumento de filtro.
- Abra
overview/OverviewViewModel.kt. Você vai ver erros no Android Studio devido às mudanças feitas na etapa anterior. AdicioneMarsApiFilter(o enum de possíveis valores de filtro) como um parâmetro à chamadagetMarsRealEstateProperties().
Importecom.example.android.marsrealestate.network.MarsApiFilterquando solicitado.
private fun getMarsRealEstateProperties(filter: MarsApiFilter) {- Modifique a chamada para
getProperties()no serviço Retrofit para transmitir essa consulta de filtro como uma string.
var getPropertiesDeferred = MarsApi.retrofitService.getProperties(filter.value)- No bloco
init {}, transmitaMarsApiFilter.SHOW_ALLcomo um argumento paragetMarsRealEstateProperties()e mostre todas as propriedades quando o app for carregado pela primeira vez.
init {
getMarsRealEstateProperties(MarsApiFilter.SHOW_ALL)
}- No final da classe, adicione um método
updateFilter()que usa um argumentoMarsApiFiltere chamagetMarsRealEstateProperties()com esse argumento.
fun updateFilter(filter: MarsApiFilter) {
getMarsRealEstateProperties(filter)
}Etapa 3: conectar o fragmento ao menu de opções
A última etapa é conectar o menu de estouro ao fragmento para chamar updateFilter() no modelo de visualização quando o usuário escolher uma opção de menu.
- Abra
res/menu/overflow_menu.xml. O app MarsRealEstate tem um menu de estouro que oferece três opções: mostrar todas as propriedades, mostrar apenas aluguéis e mostrar apenas propriedades à venda.
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/show_all_menu"
android:title="@string/show_all" />
<item
android:id="@+id/show_rent_menu"
android:title="@string/show_rent" />
<item
android:id="@+id/show_buy_menu"
android:title="@string/show_buy" />
</menu>- Abra
overview/OverviewFragment.kt. No final da classe, implemente o métodoonOptionsItemSelected()para processar as seleções de itens do menu.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
} - Em
onOptionsItemSelected(), chame o métodoupdateFilter()no modelo de visualização com o filtro adequado. Use um blocowhen {}do Kotlin para alternar entre as opções. UseMarsApiFilter.SHOW_ALLpara o valor de filtro padrão. Retornetrue, porque você processou o item de menu. ImporteMarsApiFilter(com.example.android.marsrealestate.network.MarsApiFilter) quando solicitado. O métodoonOptionsItemSelected()completo é mostrado abaixo.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
viewModel.updateFilter(
when (item.itemId) {
R.id.show_rent_menu -> MarsApiFilter.SHOW_RENT
R.id.show_buy_menu -> MarsApiFilter.SHOW_BUY
else -> MarsApiFilter.SHOW_ALL
}
)
return true
}- Compile e execute o app. Ele vai abrir a primeira grade de visão geral com todos os tipos de propriedades e as propriedades à venda marcadas com o ícone de dólar.
- Escolha Alugar no menu de opções. As propriedades são recarregadas, e nenhuma delas aparece com o ícone de dólar. (Somente imóveis para aluguel são mostrados.) Talvez seja necessário aguardar alguns instantes para que a tela seja atualizada e mostre apenas as propriedades filtradas.
- Escolha Comprar no menu de opções. As propriedades são recarregadas novamente, e todas aparecem com o ícone de dólar. (Somente propriedades à venda são mostradas.)
Agora você tem uma grade rolável de ícones para propriedades de Marte, mas é hora de ter mais detalhes. Nesta tarefa, você vai adicionar um fragmento de detalhes para mostrar os detalhes de uma propriedade específica. O fragmento de detalhes vai mostrar uma imagem maior, o preço e o tipo de propriedade, seja para aluguel ou venda.

Esse fragmento é iniciado quando o usuário toca em uma imagem na grade de visão geral. Para fazer isso, adicione um listener onClick aos itens da grade RecyclerView e navegue até o novo fragmento. Você navega acionando uma mudança de LiveData no ViewModel, como fez ao longo destas lições. Você também usa o plug-in Safe Args do componente Navigation para transmitir as informações MarsProperty selecionadas do fragmento de visão geral para o fragmento de detalhes.
Etapa 1: criar o modelo de visualização de detalhes e atualizar o layout de detalhes
Semelhante ao processo usado para o modelo de visualização e os fragmentos de visão geral, agora você precisa implementar o modelo de visualização e os arquivos de layout para o fragmento de detalhes.
- Abra
detail/DetailViewModel.kt. Assim como os arquivos Kotlin relacionados à rede estão na pastanetworke os arquivos de visão geral emoverview, a pastadetailcontém os arquivos associados à visualização de detalhes. A classeDetailViewModel(vazia no momento) usa ummarsPropertycomo parâmetro no construtor.
class DetailViewModel( marsProperty: MarsProperty,
app: Application) : AndroidViewModel(app) {
}- Na definição da classe, adicione
LiveDatapara a propriedade de Marte selecionada e exponha essas informações na visualização de detalhes. Siga o padrão usual de criar umMutableLiveDatapara manter o próprioMarsPropertye, em seguida, exponha uma propriedadeLiveDatapública imutável.
Importeandroidx.lifecycle.LiveDataeandroidx.lifecycle.MutableLiveDataquando solicitado.
private val _selectedProperty = MutableLiveData<MarsProperty>()
val selectedProperty: LiveData<MarsProperty>
get() = _selectedProperty- Crie um bloco
init {}e defina o valor da propriedade de Marte selecionada com o objetoMarsPropertydo construtor.
init {
_selectedProperty.value = marsProperty
}- Abra
res/layout/fragment_detail.xmle confira na visualização de design.
Este é o arquivo de layout do fragmento de detalhes. Ele contém umImageViewpara a foto grande, umTextViewpara o tipo de propriedade (aluguel ou venda) e umTextViewpara o preço. O layout de restrição é envolvido por umScrollViewpara rolar automaticamente se a visualização ficar muito grande para a tela, por exemplo, quando o usuário a visualiza no modo paisagem. - Acesse a guia Texto do layout. Na parte de cima do layout, logo antes do elemento
<ScrollView>, adicione um elemento<data>para associar o modelo de visualização de detalhes ao layout.
<data>
<variable
name="viewModel"
type="com.example.android.marsrealestate.detail.DetailViewModel" />
</data>- Adicione o atributo
app:imageUrlao elementoImageView. Defina como oimgSrcUrlda propriedade selecionada do modelo de visualização.
O adaptador de vinculação que carrega uma imagem usando o Glide também será usado automaticamente aqui, porque ele monitora todos os atributosapp:imageUrl.
app:imageUrl="@{viewModel.selectedProperty.imgSrcUrl}"Etapa 2: definir a navegação no modelo de visualização geral
Quando o usuário toca em uma foto no modelo de visão geral, isso deve acionar a navegação até um fragmento que mostra detalhes sobre o item clicado.
- Abra
overview/OverviewViewModel.kt. Adicione uma propriedade_navigateToSelectedPropertyMutableLiveDatae exponha-a com umLiveDataimutável.
Quando esseLiveDatamuda para não nulo, a navegação é acionada. Em breve, você vai adicionar o código para observar essa variável e acionar a navegação.
private val _navigateToSelectedProperty = MutableLiveData<MarsProperty>()
val navigateToSelectedProperty: LiveData<MarsProperty>
get() = _navigateToSelectedProperty- No final da classe, adicione um método
displayPropertyDetails()que define _navigateToSelectedPropertycomo a propriedade de Marte selecionada.
fun displayPropertyDetails(marsProperty: MarsProperty) {
_navigateToSelectedProperty.value = marsProperty
}- Adicione um método
displayPropertyDetailsComplete()que defina como nulo o valor de_navigateToSelectedProperty. Isso é necessário para marcar o estado de navegação como concluído e evitar que a navegação seja acionada novamente quando o usuário voltar da visualização de detalhes.
fun displayPropertyDetailsComplete() {
_navigateToSelectedProperty.value = null
}Etapa 3: configurar os listeners de clique no adaptador de grade e no fragmento
- Abra
overview/PhotoGridAdapter.kt. No final da classe, crie uma classeOnClickListenerpersonalizada que usa uma lambda com um parâmetromarsProperty. Dentro da classe, defina uma funçãoonClick()definida como o parâmetro lambda.
class OnClickListener(val clickListener: (marsProperty:MarsProperty) -> Unit) {
fun onClick(marsProperty:MarsProperty) = clickListener(marsProperty)
}- Role para cima até a definição da classe
PhotoGridAdaptere adicione uma propriedadeOnClickListenerparticular ao construtor.
class PhotoGridAdapter( private val onClickListener: OnClickListener ) :
ListAdapter<MarsProperty,
PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {- Para tornar uma foto clicável, adicione o
onClickListenerao item da grade no métodoonBindviewHolder(). Defina o listener de clique entre as chamadas paragetItem() and bind().
override fun onBindViewHolder(holder: MarsPropertyViewHolder, position: Int) {
val marsProperty = getItem(position)
holder.itemView.setOnClickListener {
onClickListener.onClick(marsProperty)
}
holder.bind(marsProperty)
}- Abra
overview/OverviewFragment.kt. No métodoonCreateView(), substitua a linha que inicializa a propriedadebinding.photosGrid.adapterpela linha mostrada abaixo.
Esse código adiciona o objetoPhotoGridAdapter.onClickListenerao construtorPhotoGridAdaptere chamaviewModel.displayPropertyDetails()com o objetoMarsPropertytransmitido. Isso aciona oLiveDatano modelo de visualização para a navegação.
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
viewModel.displayPropertyDetails(it)
})Etapa 4: modificar o gráfico de navegação e tornar MarsProperty parcelable
Quando um usuário toca em uma foto na grade de visão geral, o app precisa navegar até o fragmento de detalhes e transmitir os detalhes da propriedade selecionada de Marte para que a visualização detalhada possa mostrar essas informações.

No momento, você tem um listener de cliques de PhotoGridAdapter para processar o toque e uma maneira de acionar a navegação do modelo de visualização. Mas você ainda não tem um objeto MarsProperty sendo transmitido ao fragmento de detalhes. Para isso, use o Safe Args do componente de navegação.
- Abra
res/navigation/nav_graph.xml. Clique na guia Text para conferir o código XML do gráfico de navegação. - No elemento
<fragment>do fragmento de detalhes, adicione o elemento<argument>mostrado abaixo. Esse argumento, chamadoselectedProperty, tem o tipoMarsProperty.
<argument
android:name="selectedProperty"
app:argType="com.example.android.marsrealestate.network.MarsProperty"
/>- Compile o app. A navegação vai mostrar um erro porque o
MarsPropertynão é parcelable. A interfaceParcelablepermite que os objetos sejam serializados para que os dados deles possam ser transmitidos entre fragmentos ou atividades. Nesse caso, para que os dados dentro do objetoMarsPropertysejam transmitidos ao fragmento de detalhes via Safe Args,MarsPropertyprecisa implementar a interfaceParcelable. A boa notícia é que o Kotlin oferece um atalho fácil para implementar essa interface. - Abra
network/MarsProperty.kt. Adicione a anotação@Parcelizeà definição da classe.
Importekotlinx.android.parcel.Parcelizequando solicitado.
A anotação@Parcelizeusa as extensões do Kotlin para Android para implementar automaticamente os métodos na interfaceParcelablepara essa classe. Não é necessário fazer mais nada.
@Parcelize
data class MarsProperty (- Mude a definição da classe
MarsPropertypara estenderParcelable.
Importeandroid.os.Parcelablequando solicitado.
A definição da classeMarsPropertyagora é assim:
@Parcelize
data class MarsProperty (
val id: String,
@Json(name = "img_src") val imgSrcUrl: String,
val type: String,
val price: Double) : Parcelable {Etapa 5: conectar os fragmentos
Você ainda não está navegando. A navegação real acontece nos fragmentos. Nesta etapa, você vai adicionar os últimos bits para implementar a navegação entre os fragmentos de visão geral e detalhes.
- Abra
overview/OverviewFragment.kt. EmonCreateView(), abaixo das linhas que inicializam o adaptador de grade de fotos, adicione as linhas mostradas abaixo para observar onavigatedToSelectedPropertydo modelo de visualização de visão geral.
Importeandroidx.lifecycle.Observereandroidx.navigation.fragment.findNavControllerquando solicitado.
O observador testa seMarsProperty, oitna lambda, não é nulo. Se for, ele recebe o controlador de navegação do fragmento comfindNavController(). ChamedisplayPropertyDetailsComplete()para informar à ViewModel que precisa redefinir oLiveDatapara o estado nulo. Assim, você não vai acionar a navegação por engano quando o app voltar para oOverviewFragment.
viewModel.navigateToSelectedProperty.observe(this, Observer {
if ( null != it ) {
this.findNavController().navigate(
OverviewFragmentDirections.actionShowDetail(it))
viewModel.displayPropertyDetailsComplete()
}
})- Abra
detail/DetailFragment.kt. Adicione esta linha logo abaixo da chamada parasetLifecycleOwner()no métodoonCreateView(). Essa linha recebe o objetoMarsPropertyselecionado dos Safe Args.
Observe o uso do operador de asserção não nula do Kotlin (!!). Se oselectedPropertynão estiver lá, algo terrível aconteceu, e você quer que o código gere um ponteiro nulo. No código de produção, você precisa processar esse erro de alguma forma.
val marsProperty = DetailFragmentArgs.fromBundle(arguments!!).selectedProperty- Adicione esta linha em seguida para receber um novo
DetailViewModelFactory. Você vai usar oDetailViewModelFactorypara receber uma instância doDetailViewModel. O app inicial inclui uma implementação deDetailViewModelFactory. Portanto, basta inicializar.
val viewModelFactory = DetailViewModelFactory(marsProperty, application)- Por fim, adicione esta linha para receber um
DetailViewModelda fábrica e conectar todas as partes.
binding.viewModel = ViewModelProviders.of(
this, viewModelFactory).get(DetailViewModel::class.java)- Compile e execute o app. Depois, toque em qualquer foto de propriedade de Marte. O fragmento de detalhes aparece para os detalhes dessa propriedade. Toque no botão "Voltar" para retornar à página de visão geral. Observe que a tela de detalhes ainda está um pouco vazia. Você vai terminar de adicionar os dados da propriedade à página de detalhes na próxima tarefa.
No momento, a página de detalhes mostra apenas a mesma foto de Marte que você está acostumado a ver na página de visão geral. A classe MarsProperty também tem um tipo de propriedade (aluguel ou compra) e um preço. A tela de detalhes precisa incluir os dois valores, e seria útil se os imóveis para aluguel indicassem que o preço é mensal. Você usa transformações LiveData no modelo de visualização para implementar as duas coisas.
- Abra
res/values/strings.xml. O código inicial inclui recursos de string, mostrados abaixo, para ajudar você a criar as strings da visualização de detalhes. Para o preço, use o recursodisplay_price_monthly_rentaloudisplay_price, dependendo do tipo de propriedade.
<string name="type_rent">Rent</string>
<string name="type_sale">Sale</string>
<string name="display_type">For %s</string>
<string name="display_price_monthly_rental">$%,.0f/month</string>
<string name="display_price">$%,.0f</string>- Abra
detail/DetailViewModel.kt. Na parte de baixo da classe, adicione o código mostrado abaixo.
Importeandroidx.lifecycle.Transformations, se solicitado.
Essa transformação testa se a propriedade selecionada é um aluguel, usando o mesmo teste da primeira tarefa. Se a propriedade for um aluguel, a transformação vai escolher a string adequada nos recursos com uma chavewhen {}do Kotlin. As duas strings precisam de um número no final. Portanto, você concatena oproperty.pricedepois.
val displayPropertyPrice = Transformations.map(selectedProperty) {
app.applicationContext.getString(
when (it.isRental) {
true -> R.string.display_price_monthly_rental
false -> R.string.display_price
}, it.price)
}- Importe a classe
Rgerada para acessar os recursos de string no projeto.
import com.example.android.marsrealestate.R- Depois da transformação
displayPropertyPrice, adicione o código mostrado abaixo. Essa transformação concatena vários recursos de string, com base no tipo de propriedade, se é um aluguel.
val displayPropertyType = Transformations.map(selectedProperty) {
app.applicationContext.getString(R.string.display_type,
app.applicationContext.getString(
when (it.isRental) {
true -> R.string.type_rent
false -> R.string.type_sale
}))
}- Abra
res/layout/fragment_detail.xml. Só falta mais uma coisa: vincular as novas strings (criadas com as transformaçõesLiveData) à visualização de detalhes. Para isso, defina o valor do campo de texto para o texto do tipo de propriedade comoviewModel.displayPropertyTypee o campo de texto para o texto do valor do preço comoviewModel.displayPropertyPrice.
<TextView
android:id="@+id/property_type_text"
...
android:text="@{viewModel.displayPropertyType}"
...
tools:text="To Rent" />
<TextView
android:id="@+id/price_value_text"
...
android:text="@{viewModel.displayPropertyPrice}"
...
tools:text="$100,000" />- Compile e execute o app. Agora, todos os dados de propriedade aparecem na página de detalhes, bem formatados.

Projeto do Android Studio: MarsRealEstateFinal (link em inglês)
Expressões de vinculação
- Use expressões de vinculação em arquivos de layout XML para realizar operações programáticas simples, como matemática ou testes condicionais, em dados vinculados.
- Para referenciar classes dentro do arquivo de layout, use a tag
<import>dentro da tag<data>.
Opções de consulta do serviço da Web
- As solicitações para serviços da Web podem incluir parâmetros opcionais.
- Para especificar parâmetros de consulta na solicitação, use a anotação
@Queryno Retrofit.
Curso da Udacity:
Documentação do desenvolvedor Android:
- Visão geral do ViewModel
- Visão geral do LiveData
- Como vincular adaptadores
- Layouts e expressões de vinculação
- Navegação
- Primeiros passos com o componente de navegação
- Transmitir dados entre destinos (também descreve o Safe Args)
- Classe
Transformations - Classe
ViewModelProvider - Classe
ViewModelProvider.Factory
Outro:
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
O que a tag <import> faz em um arquivo de layout XML?
▢ Incluir um arquivo de layout em outro.
▢ Incorporar código do Kotlin ao arquivo de layout.
▢ Fornecer acesso a propriedades vinculadas a dados.
▢ Permitir referências a classes e membros de classes em expressões de vinculação.
Pergunta 2
Como adicionar uma opção de consulta a uma chamada de serviço da Web REST na Retrofit?
▢ Anexe a consulta ao final do URL de solicitação.
▢ Adicione um parâmetro para a consulta à função que faz a solicitação e anote esse parâmetro com @Query.
▢ Use a classe Query para criar uma solicitação.
▢ Use o método addQuery() no builder da Retrofit.
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.