Cet atelier de programmation fait partie du cours Principes de base d'Android en Kotlin. Vous tirerez pleinement parti de ce cours en suivant les ateliers de programmation dans l'ordre. Tous les ateliers de programmation du cours sont listés sur la page de destination des ateliers de programmation Principes de base d'Android en Kotlin.
Introduction
Dans les ateliers de programmation précédents de cette leçon, vous avez appris à obtenir des données sur l'immobilier sur Mars à partir d'un service Web, et à créer un RecyclerView avec une mise en page en grille pour charger et afficher les images à partir de ces données. Dans cet atelier de programmation, vous allez terminer l'application MarsRealEstate en implémentant la possibilité de filtrer les propriétés sur Mars selon qu'elles sont disponibles à la location ou à la vente. Vous allez également créer une vue détaillée pour que l'utilisateur puisse voir les détails d'une propriété s'il appuie sur sa photo dans la vue d'ensemble.
Ce que vous devez déjà savoir
- Vous savez créer et utiliser des fragments.
- Naviguer entre les fragments et utiliser Safe Args (un plug-in Gradle) pour transmettre des données entre les fragments
- Utiliser les composants d'architecture, y compris les ViewModels, les fabriques de ViewModel, les transformations et
LiveData. - Vous savez récupérer des données encodées au format JSON à partir d'un service Web REST et analyser ces données dans des objets Kotlin à l'aide des bibliothèques Retrofit et Moshi.
Points abordés
- Comment utiliser des expressions de liaison complexes dans vos fichiers de mise en page.
- Envoyer des requêtes Retrofit à un service Web en utilisant des options de requête.
Objectifs de l'atelier
- Modifiez l'application MarsRealEstate pour marquer les propriétés à vendre sur Mars (par rapport à celles à louer) avec une icône en forme de dollar.
- Utilisez le menu d'options sur la page "Vue d'ensemble" pour créer une requête de service Web qui filtre les propriétés sur Mars par type.
- Créez un fragment de détails pour une propriété Mars, connectez ce fragment à la grille d'aperçu avec la navigation et transmettez les données de la propriété à ce fragment.
Dans cet atelier de programmation (et les ateliers associés), vous allez utiliser une application appelée MarsRealEstate, qui affiche les propriétés à vendre sur Mars. Cette application se connecte à un serveur Internet pour récupérer et afficher les données de propriété, y compris des informations telles que le prix et si la propriété est disponible à la vente ou à la location. Les images représentant chaque propriété sont de vraies photos de Mars prises par les rovers de la NASA. Dans les ateliers de programmation précédents, vous avez créé un RecyclerView avec une mise en page en grille pour toutes les photos de propriétés :

Dans cette version de l'application, vous allez travailler avec le type de propriété (location ou achat) et ajouter une icône à la mise en page en grille pour marquer les propriétés à vendre :

Modifiez le menu d'options de l'application pour filtrer la grille et n'afficher que les propriétés à louer ou à vendre :

Enfin, vous allez créer une vue détaillée pour une propriété individuelle et connecter les icônes de la grille d'aperçu à ce fragment de détail avec la navigation :

Jusqu'à présent, la seule partie des données de propriété de Mars que vous avez utilisée est l'URL de l'image de la propriété. Toutefois, les données de propriété (que vous avez définies dans la classe MarsProperty) incluent également un ID, un prix et un type (location ou vente). Pour vous rafraîchir la mémoire, voici un extrait des données JSON que vous obtenez du service Web :
{
"price":8000000,
"id":"424908",
"type":"rent",
"img_src": "http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631290305226E03_DXXX.jpg"
},Dans cette tâche, vous allez commencer à travailler avec le type de propriété Mars pour ajouter une image de signe dollar aux propriétés à vendre sur la page de présentation.
Étape 1 : Mettez à jour MarsProperty pour inclure le type
La classe MarsProperty définit la structure de données pour chaque propriété fournie par le service Web. Dans un atelier de programmation précédent, vous avez utilisé la bibliothèque Moshi pour analyser la réponse JSON brute du service Web Mars dans des objets de données MarsProperty individuels.
Au cours de cette étape, vous allez ajouter une logique à la classe MarsProperty pour indiquer si une propriété est à louer ou non (c'est-à-dire si le type est la chaîne "rent" ou "buy"). Vous utiliserez cette logique à plusieurs endroits. Il est donc préférable de l'avoir ici dans la classe de données plutôt que de la répliquer.
- Ouvrez l'application MarsRealEstate du dernier atelier de programmation. (Vous pouvez télécharger MarsRealEstateGrid si vous n'avez pas l'application.)
- Ouvrez
network/MarsProperty.kt. Ajoutez un corps à la définition de classeMarsProperty, puis ajoutez un getter personnalisé pourisRentalqui renvoietruesi l'objet est de type"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"
}Étape 2 : Mettez à jour la mise en page de l'élément de grille
Vous allez maintenant mettre à jour la mise en page des éléments pour que la grille d'images n'affiche un élément graphique en forme de signe dollar que sur les images de propriétés à vendre :

Avec les expressions de liaison de données, vous pouvez effectuer ce test entièrement dans la mise en page XML des éléments de la grille.
- Ouvrez
res/layout/grid_view_item.xml. Il s'agit du fichier de mise en page pour chaque cellule individuelle de la mise en page en grille pourRecyclerView. Actuellement, le fichier ne contient que l'élément<ImageView>pour l'image de la propriété. - Dans l'élément
<data>, ajoutez un élément<import>pour la classeView. Vous utilisez des importations lorsque vous souhaitez utiliser des composants d'une classe à l'intérieur d'une expression de liaison de données dans un fichier de mise en page. Dans ce cas, vous allez utiliser les constantesView.GONEetView.VISIBLE. Vous devez donc avoir accès à la classeView.
<import type="android.view.View"/>- Entourez l'intégralité de la vue d'image avec un
FrameLayoutpour permettre à l'élément drawable du signe dollar d'être empilé au-dessus de l'image de la propriété.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="170dp">
<ImageView
android:id="@+id/mars_image"
...
</FrameLayout>- Pour
ImageView, remplacez l'attributandroid:layout_heightparmatch_parentafin de remplir le nouveauFrameLayoutparent.
android:layout_height="match_parent"- Ajoutez un deuxième élément
<ImageView>juste en dessous du premier, à l'intérieur deFrameLayout. Utilisez la définition ci-dessous. Cette image apparaît en bas à droite de l'élément de grille, au-dessus de l'image de Mars, et utilise le drawable défini dansres/drawable/ic_for_sale_outline.xmlpour l'icône du signe dollar.
<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"/>- Ajoutez l'attribut
android:visibilityà la vue d'imagemars_property_type. Utilisez une expression de liaison pour tester le type de propriété et attribuer la visibilité àView.GONE(pour une location) ouView.VISIBLE(pour un achat).
android:visibility="@{property.rental ? View.GONE : View.VISIBLE}"Jusqu'à présent, vous n'avez vu que des expressions de liaison dans les mises en page qui utilisent des variables individuelles définies dans l'élément <data>. Les expressions de liaison sont extrêmement puissantes et vous permettent d'effectuer des opérations telles que des tests et des calculs mathématiques entièrement dans votre mise en page XML. Dans ce cas, vous utilisez l'opérateur ternaire (?:) pour effectuer un test (cet objet est-il une location ?). Vous fournissez un résultat pour "true" (masquez l'icône en forme de signe dollar avec View.GONE) et un autre pour "false" (affichez cette icône avec View.VISIBLE).
Le nouveau fichier grid_view_item.xml complet est présenté ci-dessous :
<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>- Compilez et exécutez l'application. Notez que les propriétés qui ne sont pas des locations sont associées à l'icône du signe dollar.

Actuellement, votre application affiche toutes les propriétés Mars dans la grille d'aperçu. Si un utilisateur recherche une propriété locative sur Mars, il lui sera utile de voir des icônes indiquant lesquelles des propriétés disponibles sont à vendre. Toutefois, il devra toujours faire défiler la page pour parcourir les nombreuses propriétés. Dans cette tâche, vous allez ajouter un menu d'options au fragment d'aperçu qui permet à l'utilisateur d'afficher uniquement les locations, uniquement les biens à vendre ou tous les biens.

Pour ce faire, vous pouvez tester le type de chaque MarsProperty dans la grille "Vue d'ensemble" et n'afficher que les propriétés correspondantes. Toutefois, le service Web Mars réel dispose d'un paramètre ou d'une option de requête (appelé filter) qui vous permet d'obtenir uniquement les propriétés de type rent ou buy. Vous pouvez utiliser cette requête de filtre avec l'URL du service Web realestate dans un navigateur comme suit :
https://android-kotlin-fun-mars-server.appspot.com/realestate?filter=buyDans cette tâche, vous allez modifier la classe MarsApiService pour ajouter une option de requête à la requête de service Web avec Retrofit. Ensuite, vous connectez le menu d'options pour télécharger à nouveau toutes les données des propriétés sur Mars à l'aide de cette option de requête. Étant donné que la réponse que vous obtenez du service Web ne contient que les propriétés qui vous intéressent, vous n'avez pas besoin de modifier la logique d'affichage de la vue pour la grille d'aperçu.
Étape 1 : Mettez à jour le service de l'API Mars
Pour modifier la requête, vous devez revenir à la classe MarsApiService que vous avez implémentée dans le premier atelier de programmation de cette série. Vous modifiez la classe pour fournir une API de filtrage.
- Ouvrez
network/MarsApiService.kt. Juste en dessous des importations, créez unenumappeléMarsApiFilterpour définir des constantes correspondant aux valeurs de requête attendues par le service Web.
enum class MarsApiFilter(val value: String) {
SHOW_RENT("rent"),
SHOW_BUY("buy"),
SHOW_ALL("all") }- Modifiez la méthode
getProperties()pour qu'elle accepte une entrée de chaîne pour la requête de filtre, et annotez cette entrée avec@Query("filter"), comme indiqué ci-dessous.
Importezretrofit2.http.Querylorsque vous y êtes invité.
L'annotation@Queryindique à la méthodegetProperties()(et donc à Retrofit) d'envoyer la requête de service Web avec l'option de filtre. Chaque fois quegetProperties()est appelé, l'URL de la requête inclut la partie?filter=type, qui indique au service Web de répondre avec des résultats correspondant à cette requête.
fun getProperties(@Query("filter") type: String): Étape 2 : Mettez à jour le modèle de vue d'ensemble
Vous demandez des données à partir de MarsApiService dans la méthode getMarsRealEstateProperties() dans OverviewViewModel. Vous devez maintenant mettre à jour cette requête pour qu'elle accepte l'argument de filtre.
- Ouvrez
overview/OverviewViewModel.kt. Vous verrez des erreurs dans Android Studio en raison des modifications que vous avez apportées à l'étape précédente. AjoutezMarsApiFilter(l'enum des valeurs de filtre possibles) en tant que paramètre à l'appelgetMarsRealEstateProperties().
Importezcom.example.android.marsrealestate.network.MarsApiFilterlorsque vous y êtes invité.
private fun getMarsRealEstateProperties(filter: MarsApiFilter) {- Modifiez l'appel à
getProperties()dans le service Retrofit pour transmettre cette requête de filtre sous forme de chaîne.
var getPropertiesDeferred = MarsApi.retrofitService.getProperties(filter.value)- Dans le bloc
init {}, transmettezMarsApiFilter.SHOW_ALLen tant qu'argument àgetMarsRealEstateProperties()pour afficher toutes les propriétés lors du premier chargement de l'application.
init {
getMarsRealEstateProperties(MarsApiFilter.SHOW_ALL)
}- À la fin de la classe, ajoutez une méthode
updateFilter()qui accepte un argumentMarsApiFilteret appellegetMarsRealEstateProperties()avec cet argument.
fun updateFilter(filter: MarsApiFilter) {
getMarsRealEstateProperties(filter)
}Étape 3 : Connectez le fragment au menu d'options
La dernière étape consiste à connecter le menu à la fragment pour appeler updateFilter() sur le ViewModel lorsque l'utilisateur sélectionne une option de menu.
- Ouvrez
res/menu/overflow_menu.xml. L'application MarsRealEstate dispose d'un menu à trois points existant qui propose trois options : afficher toutes les propriétés, afficher uniquement les locations et afficher uniquement les propriétés à vendre.
<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>- Ouvrez
overview/OverviewFragment.kt. À la fin de la classe, implémentez la méthodeonOptionsItemSelected()pour gérer les sélections d'éléments de menu.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
} - Dans
onOptionsItemSelected(), appelez la méthodeupdateFilter()sur le modèle de vue avec le filtre approprié. Utilisez un blocwhen {}Kotlin pour basculer entre les options. UtilisezMarsApiFilter.SHOW_ALLpour la valeur de filtre par défaut. Renvoietrue, car vous avez géré l'élément de menu. Lorsque vous y êtes invité, importezMarsApiFilter(com.example.android.marsrealestate.network.MarsApiFilter). La méthodeonOptionsItemSelected()complète est présentée ci-dessous.
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
}- Compilez et exécutez l'application. L'application lance la première grille de présentation avec tous les types de propriétés et les propriétés à vendre marquées avec l'icône dollar.
- Sélectionnez Louer dans le menu d'options. Les propriétés sont rechargées et aucune d'elles n'est associée à l'icône Dollar. (Seuls les biens locatifs sont affichés.) Vous devrez peut-être patienter quelques instants pour que l'affichage s'actualise et n'affiche que les propriétés filtrées.
- Sélectionnez Acheter dans le menu d'options. Les propriétés sont à nouveau rechargées et s'affichent toutes avec l'icône dollar. (Seules les propriétés à vendre sont affichées.)
Vous disposez à présent d'une grille d'icônes défilante pour les propriétés Mars, mais il est temps d'obtenir plus de détails. Dans cette tâche, vous allez ajouter un fragment de détails pour afficher les détails d'une propriété spécifique. Le fragment de détails affiche une image plus grande, le prix et le type de propriété (location ou vente).

Ce fragment est lancé lorsque l'utilisateur appuie sur une image dans la grille d'aperçu. Pour ce faire, vous devez ajouter un écouteur onClick aux éléments de grille RecyclerView, puis accéder au nouveau fragment. Vous naviguez en déclenchant un changement LiveData dans ViewModel, comme vous l'avez fait tout au long de ces leçons. Vous utilisez également le plug-in Safe Args du composant Navigation pour transmettre les informations MarsProperty sélectionnées du fragment de présentation au fragment de détails.
Étape 1 : Créez le modèle de vue détaillée et mettez à jour la mise en page détaillée
Comme pour le ViewModel et les fragments de la vue d'ensemble, vous devez maintenant implémenter le ViewModel et les fichiers de mise en page pour le fragment d'informations.
- Ouvrez
detail/DetailViewModel.kt. Tout comme les fichiers Kotlin liés au réseau sont contenus dans le dossiernetworket les fichiers de présentation dansoverview, le dossierdetailcontient les fichiers associés à la vue détaillée. Notez que la classeDetailViewModel(vide pour le moment) accepte unmarsPropertycomme paramètre dans le constructeur.
class DetailViewModel( marsProperty: MarsProperty,
app: Application) : AndroidViewModel(app) {
}- Dans la définition de la classe, ajoutez
LiveDatapour la propriété Mars sélectionnée afin d'exposer ces informations dans la vue détaillée. Suivez le schéma habituel de création d'unMutableLiveDatapour contenir leMarsPropertylui-même, puis exposez une propriétéLiveDatapublique immuable.
Importezandroidx.lifecycle.LiveDataetandroidx.lifecycle.MutableLiveDatalorsque vous y êtes invité.
private val _selectedProperty = MutableLiveData<MarsProperty>()
val selectedProperty: LiveData<MarsProperty>
get() = _selectedProperty- Créez un bloc
init {}et définissez la valeur de la propriété Mars sélectionnée avec l'objetMarsPropertydu constructeur.
init {
_selectedProperty.value = marsProperty
}- Ouvrez
res/layout/fragment_detail.xmlet examinez-le dans la vue Conception.
Il s'agit du fichier de mise en page du fragment de détail. Il contient unImageViewpour la grande photo, unTextViewpour le type de propriété (location ou vente) et unTextViewpour le prix. Notez que la mise en page des contraintes est encapsulée dans unScrollView. Elle défilera donc automatiquement si la vue devient trop grande pour l'écran, par exemple lorsque l'utilisateur la consulte en mode paysage. - Accédez à l'onglet Texte de la mise en page. En haut de la mise en page, juste avant l'élément
<ScrollView>, ajoutez un élément<data>pour associer le ViewModel de détails à la mise en page.
<data>
<variable
name="viewModel"
type="com.example.android.marsrealestate.detail.DetailViewModel" />
</data>- Ajoutez l'attribut
app:imageUrlà l'élémentImageView. Définissez-le surimgSrcUrlà partir de la propriété sélectionnée du modèle de vue.
L'adaptateur de liaison qui charge une image à l'aide de Glide sera également utilisé ici automatiquement, car cet adaptateur surveille tous les attributsapp:imageUrl.
app:imageUrl="@{viewModel.selectedProperty.imgSrcUrl}"Étape 2 : Définissez la navigation dans le modèle de vue d'ensemble
Lorsque l'utilisateur appuie sur une photo dans le modèle d'aperçu, il doit être redirigé vers un fragment affichant des informations sur l'élément sélectionné.
- Ouvrez
overview/OverviewViewModel.kt. Ajoutez une propriété_navigateToSelectedPropertyMutableLiveDataet exposez-la avec unLiveDataimmuable.
Lorsque ceLiveDatapasse à une valeur non nulle, la navigation est déclenchée. (Vous ajouterez bientôt le code pour observer cette variable et déclencher la navigation.)
private val _navigateToSelectedProperty = MutableLiveData<MarsProperty>()
val navigateToSelectedProperty: LiveData<MarsProperty>
get() = _navigateToSelectedProperty- À la fin de la classe, ajoutez une méthode
displayPropertyDetails()qui définit _navigateToSelectedPropertysur la propriété Mars sélectionnée.
fun displayPropertyDetails(marsProperty: MarsProperty) {
_navigateToSelectedProperty.value = marsProperty
}- Ajoutez une méthode
displayPropertyDetailsComplete()qui définit la valeur de_navigateToSelectedPropertysur "null". Vous en avez besoin pour marquer l'état de navigation comme terminé et pour éviter que la navigation ne soit déclenchée à nouveau lorsque l'utilisateur revient de la vue détaillée.
fun displayPropertyDetailsComplete() {
_navigateToSelectedProperty.value = null
}Étape 3 : Configurez les écouteurs de clics dans l'adaptateur de grille et le fragment
- Ouvrez
overview/PhotoGridAdapter.kt. À la fin de la classe, créez une classeOnClickListenerpersonnalisée qui accepte un lambda avec un paramètremarsProperty. Dans la classe, définissez une fonctiononClick()définie sur le paramètre lambda.
class OnClickListener(val clickListener: (marsProperty:MarsProperty) -> Unit) {
fun onClick(marsProperty:MarsProperty) = clickListener(marsProperty)
}- Faites défiler la page jusqu'à la définition de la classe
PhotoGridAdapteret ajoutez une propriétéOnClickListenerprivée au constructeur.
class PhotoGridAdapter( private val onClickListener: OnClickListener ) :
ListAdapter<MarsProperty,
PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {- Rendez une photo cliquable en ajoutant
onClickListenerà l'élément de grille dans la méthodeonBindviewHolder(). Définissez l'écouteur de clics entre les appels àgetItem() and bind().
override fun onBindViewHolder(holder: MarsPropertyViewHolder, position: Int) {
val marsProperty = getItem(position)
holder.itemView.setOnClickListener {
onClickListener.onClick(marsProperty)
}
holder.bind(marsProperty)
}- Ouvrez
overview/OverviewFragment.kt. Dans la méthodeonCreateView(), remplacez la ligne qui initialise la propriétébinding.photosGrid.adapterpar celle indiquée ci-dessous.
Ce code ajoute l'objetPhotoGridAdapter.onClickListenerau constructeurPhotoGridAdapteret appelleviewModel.displayPropertyDetails()avec l'objetMarsPropertytransmis. Cela déclenche leLiveDatadans le modèle de vue pour la navigation.
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
viewModel.displayPropertyDetails(it)
})Étape 4 : Modifiez le graphique de navigation et rendez MarsProperty sérialisable
Lorsqu'un utilisateur appuie sur une photo dans la grille d'aperçu, l'application doit accéder au fragment de détails et transmettre les détails de la propriété Mars sélectionnée afin que la vue détaillée puisse afficher ces informations.

Pour l'instant, vous disposez d'un écouteur de clics de PhotoGridAdapter pour gérer l'appui et d'un moyen de déclencher la navigation à partir du ViewModel. Toutefois, aucun objet MarsProperty n'est encore transmis au fragment de détails. Pour cela, vous utilisez Safe Args du composant Navigation.
- Ouvrez
res/navigation/nav_graph.xml. Cliquez sur l'onglet Texte pour afficher le code XML du graphique de navigation. - Dans l'élément
<fragment>du fragment de détails, ajoutez l'élément<argument>indiqué ci-dessous. Cet argument, appeléselectedProperty, est de typeMarsProperty.
<argument
android:name="selectedProperty"
app:argType="com.example.android.marsrealestate.network.MarsProperty"
/>- Compilez l'application. La navigation génère une erreur, car
MarsPropertyn'est pas parcelable. L'interfaceParcelablepermet de sérialiser les objets afin que leurs données puissent être transmises entre les fragments ou les activités. Dans ce cas, pour que les données de l'objetMarsPropertysoient transmises au fragment de détails via Safe Args,MarsPropertydoit implémenter l'interfaceParcelable. La bonne nouvelle est que Kotlin fournit un raccourci simple pour implémenter cette interface. - Ouvrez
network/MarsProperty.kt. Ajoutez l'annotation@Parcelizeà la définition de la classe.
Importezkotlinx.android.parcel.Parcelizelorsque vous y êtes invité.
L'annotation@Parcelizeutilise les extensions Kotlin Android pour implémenter automatiquement les méthodes de l'interfaceParcelablepour cette classe. Aucune autre action de votre part n'est requise.
@Parcelize
data class MarsProperty (- Modifiez la définition de la classe
MarsPropertypour étendreParcelable.
Importezandroid.os.Parcelablelorsque vous y êtes invité.
La définition de la classeMarsPropertyressemble maintenant à ceci :
@Parcelize
data class MarsProperty (
val id: String,
@Json(name = "img_src") val imgSrcUrl: String,
val type: String,
val price: Double) : Parcelable {Étape 5 : Connectez les fragments
Vous ne naviguez toujours pas. La navigation proprement dite a lieu dans les fragments. Dans cette étape, vous allez ajouter les derniers éléments pour implémenter la navigation entre les fragments d'aperçu et de détails.
- Ouvrez
overview/OverviewFragment.kt. DansonCreateView(), sous les lignes qui initialisent l'adaptateur pour grille de photos, ajoutez les lignes ci-dessous pour observernavigatedToSelectedPropertyà partir du ViewModel de la vue d'ensemble.
Importezandroidx.lifecycle.Observeretandroidx.navigation.fragment.findNavControllerlorsque vous y êtes invité.
L'observateur vérifie siMarsProperty(leitdans le lambda) n'est pas nul. Si c'est le cas, il obtient le contrôleur de navigation à partir du fragment avecfindNavController(). AppelezdisplayPropertyDetailsComplete()pour indiquer au ViewModel de réinitialiserLiveDataà l'état nul. Vous ne déclencherez ainsi pas accidentellement une nouvelle navigation lorsque l'application reviendra àOverviewFragment.
viewModel.navigateToSelectedProperty.observe(this, Observer {
if ( null != it ) {
this.findNavController().navigate(
OverviewFragmentDirections.actionShowDetail(it))
viewModel.displayPropertyDetailsComplete()
}
})- Ouvrez
detail/DetailFragment.kt. Ajoutez cette ligne juste en dessous de l'appel àsetLifecycleOwner()dans la méthodeonCreateView(). Cette ligne récupère l'objetMarsPropertysélectionné à partir de Safe Args.
Notez l'utilisation de l'opérateur d'assertion non nul de Kotlin (!!). SiselectedPropertyn'est pas là, quelque chose de terrible s'est produit et vous voulez en fait que le code génère un pointeur Null. (Dans le code de production, vous devez gérer cette erreur d'une manière ou d'une autre.)
val marsProperty = DetailFragmentArgs.fromBundle(arguments!!).selectedProperty- Ajoutez ensuite cette ligne pour obtenir un nouveau
DetailViewModelFactory. Vous utiliserezDetailViewModelFactorypour obtenir une instance deDetailViewModel. L'application de démarrage inclut une implémentation deDetailViewModelFactory. Il vous suffit donc de l'initialiser.
val viewModelFactory = DetailViewModelFactory(marsProperty, application)- Enfin, ajoutez cette ligne pour obtenir un
DetailViewModelde l'usine et pour connecter toutes les pièces.
binding.viewModel = ViewModelProviders.of(
this, viewModelFactory).get(DetailViewModel::class.java)- Compilez et exécutez l'application, puis appuyez sur une photo de propriété de Mars. Le fragment de détails s'affiche pour les détails de cette propriété. Appuyez sur le bouton Retour pour revenir à la page d'aperçu et remarquez que l'écran de détails est encore un peu vide. Dans la tâche suivante, vous terminerez d'ajouter les données de la propriété à cette page d'informations.
Pour l'instant, la page d'informations n'affiche que la même photo de Mars que celle que vous avez l'habitude de voir sur la page de présentation. La classe MarsProperty possède également un type de propriété (location ou achat) et un prix. L'écran d'informations doit inclure ces deux valeurs. Il serait également utile que les propriétés locatives indiquent que le prix est une valeur mensuelle. Vous utilisez des transformations LiveData dans le modèle de vue pour implémenter ces deux éléments.
- Ouvrez
res/values/strings.xml. Le code de démarrage inclut des ressources de chaîne, présentées ci-dessous, pour vous aider à créer les chaînes de la vue détaillée. Pour le prix, vous utiliserez la ressourcedisplay_price_monthly_rentalou la ressourcedisplay_price, selon le type de propriété.
<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>- Ouvrez
detail/DetailViewModel.kt. En bas de la classe, ajoutez le code ci-dessous.
Si vous y êtes invité, importezandroidx.lifecycle.Transformations.
Cette transformation teste si la propriété sélectionnée est une location, en utilisant le même test que dans la première tâche. Si la propriété est une location, la transformation choisit la chaîne appropriée à partir des ressources avec un commutateur Kotlinwhen {}. Ces deux chaînes doivent comporter un nombre à la fin. Vous devez donc concaténerproperty.priceaprès.
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)
}- Importez la classe
Rgénérée pour accéder aux ressources de chaîne du projet.
import com.example.android.marsrealestate.R- Après la transformation
displayPropertyPrice, ajoutez le code ci-dessous. Cette transformation concatène plusieurs ressources de chaîne, selon que le type de propriété est une location ou non.
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
}))
}- Ouvrez
res/layout/fragment_detail.xml. Il ne vous reste plus qu'à lier les nouvelles chaînes (que vous avez créées avec les transformationsLiveData) à la vue détaillée. Pour ce faire, définissez la valeur du champ de texte pour le texte du type de propriété surviewModel.displayPropertyTypeet celle du champ de texte pour le texte de la valeur du prix surviewModel.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" />- Compilez et exécutez l'application. Toutes les données de propriété s'affichent désormais sur la page d'informations, dans un format agréable.

Projet Android Studio : MarsRealEstateFinal
Expressions de liaison
- Utilisez des expressions de liaison dans les fichiers de mise en page XML pour effectuer des opérations programmatiques simples, telles que des calculs ou des tests conditionnels, sur les données liées.
- Pour référencer des classes dans votre fichier de mise en page, utilisez la balise
<import>à l'intérieur de la balise<data>.
Options de requête de service Web
- Les requêtes adressées aux services Web peuvent inclure des paramètres facultatifs.
- Pour spécifier des paramètres de requête dans la requête, utilisez l'annotation
@Querydans Retrofit.
Cours Udacity :
Documentation pour les développeurs Android :
- Présentation de ViewModel
- Présentation de LiveData
- Adaptateurs de liaison
- Dispositions et expressions de liaison
- Navigation
- Premiers pas avec le composant Navigation
- Transmettre des données entre les destinations (décrit également Safe Args)
- Classe
Transformations - Classe
ViewModelProvider - Classe
ViewModelProvider.Factory
Autre :
Cette section répertorie les devoirs possibles pour les élèves qui suivent cet atelier de programmation dans le cadre d'un cours animé par un enseignant. Il revient à l'enseignant d'effectuer les opérations suivantes :
- Attribuer des devoirs si nécessaire
- Indiquer aux élèves comment rendre leurs devoirs
- Noter les devoirs
Les enseignants peuvent utiliser ces suggestions autant qu'ils le souhaitent, et ne doivent pas hésiter à attribuer d'autres devoirs aux élèves s'ils le jugent nécessaire.
Si vous suivez cet atelier de programmation par vous-même, n'hésitez pas à utiliser ces devoirs pour tester vos connaissances.
Répondre aux questions suivantes
Question 1
À quoi sert la balise <import> dans un fichier de mise en page XML ?
▢ Incluez un fichier de mise en page dans un autre.
▢ Intégrez le code Kotlin dans le fichier de mise en page.
▢ Fournir un accès aux propriétés liées aux données.
▢ Vous permet de référencer des classes et des membres de classe dans des expressions de liaison.
Question 2
Comment ajouter une option de requête à un appel de service Web REST dans Retrofit ?
▢ Ajoutez la requête à la fin de l'URL de requête.
▢ Ajoutez un paramètre à la fonction qui effectue la requête, puis annotez-le avec @Query.
▢ Utilisez la classe Query pour créer une requête.
▢ Utilisez la méthode addQuery() dans le compilateur Retrofit.
Passez à la leçon suivante :
Pour obtenir des liens vers d'autres ateliers de programmation de ce cours, consultez la page de destination des ateliers de programmation Principes de base d'Android en Kotlin.