Cet atelier de programmation fait partie du cours Android Kotlin Fundamentals. Vous tirerez le meilleur parti de ce cours si vous suivez les ateliers de programmation dans l'ordre. Tous les ateliers de programmation du cours sont répertoriés sur la page de destination des ateliers de programmation Android Kotlin Fundamentals.
Présentation
Dans les précédents ateliers de programmation 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 des images à partir de ces données. Dans cet atelier de programmation, vous allez terminer l'application MarsRealEstate en mettant en œuvre la possibilité de filtrer les propriétés Mars selon qu'elles sont disponibles à la location ou à la vente. Vous pouvez également créer une vue détaillée de sorte que si l'utilisateur appuie sur une photo de propriété dans la vue d'ensemble, il voit une vue détaillée de cette propriété.
Ce que vous devez déjà savoir
- 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
- Utilisation des composants d'architecture, y compris afficher les modèles, afficher les usines de modèles, les transformations et
LiveData
. - Récupérez des données encodées au format JSON à partir d'un service Web REST, et analysez 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
- Faire des requêtes Retrofit à un service Web avec des options de requête.
Objectifs de l'atelier
- Modifiez l'application MarsRealEstate pour indiquer qu'une icône en forme de dollar est utilisée pour les propriétés à vendre sur Mars (plutôt que celles en location).
- Utilisez le menu d'options de la page "Vue d'ensemble" pour créer une requête de service Web qui filtre les propriétés Mars par type.
- Créez un fragment des détails pour une propriété Mars. Associez ce fragment à la grille de présentation avec la barre de navigation et transmettez les données de la propriété à ce fragment.
Dans cet atelier de programmation (et dans les ateliers de programmation associés), vous travaillez avec une application appelée MarsRealEstate, qui affiche des propriétés à vendre sur Mars. Cette application se connecte à un serveur Internet pour récupérer et afficher les données sur les établissements, par exemple leur prix et leur disponibilité en vente ou en location. Les images de chaque établissement sont des photos réelles de Mars collectées par les rovers sur Mars. Dans les précédents ateliers de programmation, vous avez créé un RecyclerView
avec une mise en page en grille pour toutes les photos de la propriété:
Dans cette version de l'application, vous utilisez le type de propriété (location ou achat) et vous ajoutez une icône à la mise en page en grille afin de marquer les propriétés à vendre:
Vous pouvez modifier le menu d'options de l'application pour filtrer la grille afin de n'afficher que les propriétés à louer ou à vendre.
Enfin, vous allez créer une vue détaillée d'une propriété individuelle, puis associer les icônes de la grille de présentation à ce fragment de détail à l'aide de la navigation:
Jusqu'à présent, la seule partie des données de propriété Mars que vous avez utilisée était l'URL de l'image de la propriété. Cependant, les données de propriété, que vous avez définies dans la classe MarsProperty
, comprennent également un ID, un prix et un type (location ou vente). Pour actualiser votre mémoire, voici un extrait des données JSON fournies par le 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 utiliser le type de propriété Mars pour ajouter une image de symbole dollar aux propriétés en vente sur la page Vue d'ensemble.
Étape 1: Mettez à jour MarsProperty pour inclure le type
La classe MarsProperty
définit la structure des 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.
Dans 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 allez utiliser cette logique à plusieurs endroits. Il est donc préférable de la placer dans la classe de données plutôt que de la répliquer.
- Ouvrez l'application MarsRealEstate à partir du dernier atelier de programmation. (Vous pouvez télécharger MarsRealEstateGrid si vous ne disposez pas de cette application.)
- Ouvrez
network/MarsProperty.kt
. Ajoutez un corps à la définition de la classeMarsProperty
, ainsi qu'un getter personnalisé pourisRental
qui renvoietrue
si 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 des éléments de la grille
À présent, mettez à jour la mise en page des éléments pour la grille d'images afin d'afficher un signe dollar pouvant être dessiné uniquement sur ces images de propriétés en vente:
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 de chaque cellule dans la mise en page en grille dansRecyclerView
. Actuellement, le fichier ne contient que l'élément<ImageView>
de l'image de la propriété. - Dans l'élément
<data>
, ajoutez un élément<import>
pour la classeView
. Les importations permettent d'utiliser les composants d'une classe dans une expression de liaison de données dans un fichier de mise en page. Dans ce cas, vous allez utiliser les constantesView.GONE
etView.VISIBLE
. Vous devez donc accéder à la classeView
.
<import type="android.view.View"/>
- Entourez l'ensemble de la vue d'image avec un
FrameLayout
, pour permettre à l'utilisateur de dessiner le dollar en superposition sur 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_height
parmatch_parent
pour remplir le nouveau parentFrameLayout
.
android:layout_height="match_parent"
- Ajoutez un deuxième élément
<ImageView>
juste en dessous du premier, dans la sectionFrameLayout
. Utilisez la définition affichée ci-dessous. Cette image apparaît en bas à droite de l'élément de la grille, au-dessus de l'image de Mars, et utilise le drawable défini dansres/drawable/ic_for_sale_outline.xml
pour l'icône en forme de 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é, puis attribuez-la àView.GONE
(pour une location) ou àView.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 performantes. Elles vous permettent d'effectuer des opérations telles que des tests et des calculs mathématiques dans votre mise en page XML. Dans ce cas, vous allez utiliser l'opérateur ternary (?:
) pour effectuer un test (est-ce que cet objet est une location ?). Vous fournissez un résultat pour "true" (masquer l'icône en forme de dollar avec View.GONE
) et un autre pour "false" (afficher cette icône avec View.VISIBLE
).
Le nouveau fichier grid_view_item.xml
complet est affiché 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 l'icône en forme de signe dollar est ajoutée aux propriétés qui ne sont pas des locations.
Actuellement, votre application affiche toutes les propriétés Mars dans la grille d'aperçu. Si un internaute recherche un bien à louer sur Mars, les icônes qui indiquent les propriétés disponibles à la vente peuvent s'avérer utiles, mais il existe encore de nombreux établissements à faire défiler sur la page. Dans cette tâche, vous allez ajouter un menu d'options au fragment de présentation, qui permet à l'utilisateur de n'afficher que les locations, les propriétés à vendre uniquement ou tous les éléments.
Pour effectuer cette tâche, vous pouvez tester le type de chaque MarsProperty
dans la grille d'aperçu et n'afficher que les propriétés correspondantes. Cependant, le service Web de Mars comporte un paramètre ou une option de requête (appelé filter
) qui vous permet d'obtenir uniquement des propriétés de type rent
ou de type buy
. Vous pouvez utiliser cette requête de filtre avec l'URL du service Web realestate
dans un navigateur de ce type:
https://android-kotlin-fun-mars-server.appspot.com/realestate?filter=buy
Dans cette tâche, vous allez modifier la classe MarsApiService
pour ajouter une option de requête à la requête de service Web avec Retrofit. Vous devez ensuite associer le menu d'options pour télécharger à nouveau toutes les données de la propriété Mars à l'aide de cette option de requête. Comme la réponse obtenue dans le 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 grille.
Étape 1: Mettez à jour le service de l'API Mars
Pour modifier la requête, vous devez reconsulter la classe MarsApiService
que vous avez implémentée dans le premier atelier de programmation de cette série. Modifiez la classe pour fournir une API de filtrage.
- Ouvrez
network/MarsApiService.kt
. Juste en dessous des importations, créez unenum
appeléMarsApiFilter
pour définir des constantes correspondant aux valeurs de requêtes 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()
de façon à accepter la chaîne saisie pour la requête de filtre et à annoter cette entrée avec@Query("filter")
, comme indiqué ci-dessous.
Importezretrofit2.http.Query
lorsque vous y êtes invité.
L'annotation@Query
indique à la méthodegetProperties()
(et donc à Retrofit) d'effectuer 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 redirige le service Web pour qu'il réponde avec des résultats correspondant à cette requête.
fun getProperties(@Query("filter") type: String):
Étape 2: Mettez à jour le modèle de la 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 utiliser l'argument de filtre.
- Ouvrez
overview/OverviewViewModel.kt
. Des erreurs s'afficheront dans Android Studio en raison des modifications que vous avez apportées à l'étape précédente. AjoutezMarsApiFilter
(énumération des valeurs de filtre possibles) comme paramètre à l'appel degetMarsRealEstateProperties()
.
Importezcom.example.android.marsrealestate.network.MarsApiFilter
lorsque vous le demandez.
private fun getMarsRealEstateProperties(filter: MarsApiFilter) {
- Modifiez l'appel de
getProperties()
dans le service Retrofit pour transmettre cette requête de filtre en tant que chaîne.
var getPropertiesDeferred = MarsApi.retrofitService.getProperties(filter.value)
- Dans le bloc
init {}
, transmettezMarsApiFilter.SHOW_ALL
en 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 utilise un argumentMarsApiFilter
et appellegetMarsRealEstateProperties()
avec cet argument.
fun updateFilter(filter: MarsApiFilter) {
getMarsRealEstateProperties(filter)
}
Étape 3: Connectez le fragment au menu d'options
La dernière étape consiste à associer le menu à développer au fragment afin d'appeler updateFilter()
sur le modèle de vue lorsque l'utilisateur choisit une option de menu.
- Ouvrez
res/menu/overflow_menu.xml
. L'application MarsRealEstate comporte un menu à développer qui propose trois options: afficher toutes les propriétés, n'afficher que 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 passer d'une option à l'autre. UtilisezMarsApiFilter.SHOW_ALL
pour la valeur de filtre par défaut. Renvoyeztrue
, car vous avez géré l'élément de menu. ImportezMarsApiFilter
(com.example.android.marsrealestate.network.MarsApiFilter
) à la demande. La méthodeonOptionsItemSelected()
complète est illustré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 comprenant tous les types de propriétés, ainsi que les propriétés à vendre signalées par l'icône en forme de dollar.
- Sélectionnez Louer dans le menu d'options. Les propriétés sont actualisées, et aucune d'entre elles n'apparaît avec l'icône en forme de dollar. (Seules les propriétés de location sont affichées.) Vous devrez peut-être patienter quelques instants avant que l'écran n'affiche les propriétés filtrées.
- Sélectionnez Acheter dans le menu d'options. Les propriétés sont à nouveau actualisées, et toutes sont accompagnées de l'icône en forme de dollar. (Seules les propriétés à vendre sont affichées.)
Vous disposez maintenant d'une grille d'icônes qui défile pour les propriétés Mars, mais il est temps d'obtenir plus d'informations. Dans cette tâche, vous allez ajouter un fragment pour afficher les détails d'une propriété spécifique. Le fragment des détails affiche une image plus grande, le prix et le type d'établissement (qu'il s'agisse d'une location ou d'une vente).
Ce fragment est lancé lorsque l'utilisateur appuie sur une image de la grille de présentation. Pour ce faire, vous devez ajouter un écouteur onClick
aux éléments de la grille RecyclerView
, puis accéder au nouveau fragment. Pour naviguer, vous devez déclencher une modification de LiveData
dans le ViewModel
, comme vous l'avez fait au cours de ces cours. Vous utilisez également le plug-in Safe Args du composant de navigation pour transmettre les informations MarsProperty
sélectionnées du fragment de présentation au fragment de détail.
Étape 1: Créer un modèle détaillé
De la même manière que pour le modèle de vue d'aperçu et les fragments, vous devez à présent implémenter les fichiers de modèle de vue et de mise en page pour le fragment des détails.
- Ouvrez
detail/DetailViewModel.kt
. Tout comme les fichiers Kotlin liés au réseau sont contenus dans le dossiernetwork
et les fichiers de présentation dansoverview
, le dossierdetail
contient les fichiers associés à la vue détaillée. Notez que la classeDetailViewModel
(vide actuellement) utilise unmarsProperty
comme paramètre dans le constructeur.
class DetailViewModel( marsProperty: MarsProperty,
app: Application) : AndroidViewModel(app) {
}
- Dans la définition de classe, ajoutez
LiveData
pour la propriété Mars sélectionnée afin d'afficher ces informations dans la vue détaillée. Respectez le schéma habituel de création d'unMutableLiveData
pour contenir leMarsProperty
lui-même, puis exposez une propriétéLiveData
publique immuable.
Importezandroidx.lifecycle.LiveData
et importezandroidx.lifecycle.MutableLiveData
à la demande.
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'objetMarsProperty
du constructeur.
init {
_selectedProperty.value = marsProperty
}
- Ouvrez
res/layout/fragment_detail.xml
et regardez-le dans la vue Conception.
Il s'agit du fichier de mise en page du fragment détaillé. Il contient unImageView
pour la grande photo, unTextView
pour le type d'établissement (location ou vente) et unTextView
pour le prix. Notez que la mise en page de la contrainte est encapsulée avec uneScrollView
afin qu'elle défile automatiquement si l'affichage devient trop volumineux, par exemple lorsque l'utilisateur l'affiche 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 modèle de vue détaillée à 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-la 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é automatiquement ici, car il 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 "Overview", il déclenche la navigation vers un fragment qui affiche les détails de l'élément sur lequel l'utilisateur a cliqué.
- Ouvrez
overview/OverviewViewModel.kt
. Ajoutez une propriété_navigateToSelectedProperty
MutableLiveData
et exposez-la avec une propriétéLiveData
immuable.
Lorsque ce paramètreLiveData
est défini sur une valeur non nulle, la navigation est déclenchée. (Bientôt, vous ajouterez le code pour observer cette variable et déclencher la navigation).
private val _navigateToSelectedProperty = MutableLiveData<MarsProperty>()
val navigateToSelectedProperty: LiveData<MarsProperty>
get() = _navigateToSelectedProperty
- En fin de classe, ajoutez une méthode
displayPropertyDetails()
qui définit _navigateToSelectedProperty
sur la propriété Mars sélectionnée.
fun displayPropertyDetails(marsProperty: MarsProperty) {
_navigateToSelectedProperty.value = marsProperty
}
- Ajoutez une méthode
displayPropertyDetailsComplete()
qui renvoie la valeur_navigateToSelectedProperty
. Vous en avez besoin pour marquer l'état de navigation comme terminé et pour éviter que la navigation ne se déclenche de 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 en grille et le fragment
- Ouvrez
overview/PhotoGridAdapter.kt
. À la fin de la classe, créez une classeOnClickListener
personnalisée qui utilise 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 classe de
PhotoGridAdapter
, puis ajoutez une propriétéOnClickListener
privée au constructeur.
class PhotoGridAdapter( private val onClickListener: OnClickListener ) :
ListAdapter<MarsProperty,
PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
- Rendez une photo cliquable en ajoutant le
onClickListener
à l'élément de la 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.adapter
par la ligne affichée ci-dessous.
Ce code ajoute l'objetPhotoGridAdapter.onClickListener
au constructeurPhotoGridAdapter
et appelleviewModel.displayPropertyDetails()
avec l'objetMarsProperty
transmis. Cela déclenche leLiveData
dans le modèle de vue de la navigation.
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
viewModel.displayPropertyDetails(it)
})
Étape 4: Modifiez le graphique de navigation et activez l'envoi de fichiers par MarsProperty
Lorsqu'un utilisateur appuie sur une photo dans la grille d'aperçu, l'application devrait accéder au fragment des détails et transmettre les informations de la propriété Mars sélectionnée afin que la vue détaillée puisse afficher ces informations.
Pour le moment, un écouteur de clics PhotoGridAdapter
gère l'appui et permet de déclencher la navigation à partir du modèle de vue. Or, aucun objet MarsProperty
n'est encore transmis au fragment de détail. Pour ce faire, utilisez l'outil Safe Args à partir du composant de navigation.
- Ouvrez
res/navigation/nav_graph.xml
. Cliquez sur l'onglet Text (Texte) pour afficher le code XML du graphique de navigation. - Dans l'élément
<fragment>
du fragment détaillé, ajoutez l'élément<argument>
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 le
MarsProperty
n'est pas parcelable. L'interfaceParcelable
permet de sérialiser des objets, de sorte que les données puissent être transmises entre des fragments ou des activités. Dans ce cas, pour que les données de l'objetMarsProperty
soient transmises au fragment de détails via Safe Args,MarsProperty
doit implémenter l'interfaceParcelable
. La bonne nouvelle, c'est que Kotlin propose un raccourci facile pour implémenter cette interface. - Ouvrez
network/MarsProperty.kt
. Ajoutez l'annotation@Parcelize
à la définition de classe.
Importezkotlinx.android.parcel.Parcelize
lorsque vous y êtes invité.
L'annotation@Parcelize
utilise les extensions Android Kotlin pour intégrer automatiquement les méthodes dans l'interfaceParcelable
de cette classe. Aucune action n'est requise de votre part.
@Parcelize
data class MarsProperty (
- Modifiez la définition de la classe
MarsProperty
pour étendreParcelable
.
Importezandroid.os.Parcelable
lorsque vous l'avez demandé.
La définition de la classeMarsProperty
ressemble 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
La navigation n'est toujours pas terminée. La navigation s'effectue dans les fragments. Au cours de cette étape, vous allez ajouter les derniers bits pour la mise en œuvre de la navigation entre les fragments "Overview" (Vue d'ensemble) et "Détail".
- Ouvrez
overview/OverviewFragment.kt
. DansonCreateView()
, sous les lignes qui initialisent l'adaptateur de la grille de photos, ajoutez les lignes ci-dessous pour observer lenavigatedToSelectedProperty
du modèle de vue d'ensemble.
Importezandroidx.lifecycle.Observer
et importezandroidx.navigation.fragment.findNavController
à la demande.
L'observateur teste siMarsProperty
(leit
du lambda) n'est pas nul et, le cas échéant, il obtient le contrôleur de navigation du fragment avecfindNavController()
. AppelezdisplayPropertyDetailsComplete()
pour indiquer au modèle de vue de réinitialiser la valeurLiveData
. Ainsi, vous ne déclencherez pas accidentellement une nouvelle navigation lorsque l'application reviendra auOverviewFragment
.
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 desetLifecycleOwner()
dans la méthodeonCreateView()
. Cette ligne obtient l'objetMarsProperty
sélectionné à partir des arguments sécurisés.
Observez l'utilisation de l'opérateur d'assertion "non-null" (!!
). Si l'instructionselectedProperty
n'est pas là, un problème est survenu et vous souhaitez que le code génère un pointeur nul. (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 allez utiliserDetailViewModelFactory
pour obtenir une instance deDetailViewModel
. L'application de départ inclut une implémentation deDetailViewModelFactory
. Il vous suffit donc de l'initialiser ici.
val viewModelFactory = DetailViewModelFactory(marsProperty, application)
- Enfin, ajoutez cette ligne pour obtenir un
DetailViewModel
depuis la fabrique et connecter toutes les pièces.
binding.viewModel = ViewModelProviders.of(
this, viewModelFactory).get(DetailViewModel::class.java)
- Compilez et exécutez l'application, puis appuyez sur n'importe quelle photo de propriété Mars. Le fragment des détails s'affiche pour les détails de cette propriété. Appuyez sur le bouton "Retour" pour revenir à la page de présentation, et notez que l'écran d'informations est encore sommaire. Vous aurez terminé d'ajouter les données de la propriété à cette page d'informations lors de la tâche suivante.
Pour le moment, la page d'informations n'affiche que la photo de Mars que vous voyez sur la page de présentation. La classe MarsProperty
comporte également un type de propriété (location ou achat) et un prix de propriété. L'écran détaillé doit inclure ces deux valeurs. Il peut être utile si les propriétés de location indiquent que le prix est une valeur mensuelle. Vous utilisez les 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 devez utiliser la ressourcedisplay_price_monthly_rental
ou 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 indiqué ci-dessous.
Importezandroidx.lifecycle.Transformations
si nécessaire.
Cette transformation teste si la propriété sélectionnée est une location, en utilisant le même test que celui effectué à la première tâche. Si la propriété est une location, la transformation choisit la chaîne appropriée dans les ressources à l'aide d'un commutateurwhen {}
Kotlin. Ces deux chaînes ont besoin d'un numéro à la fin. Vous concaténez doncproperty.price
.
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
R
gé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 affiché ci-dessous. Cette transformation concatène plusieurs ressources de chaîne, selon que le type d'établissement est une location.
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'une tâche à accomplir pour lier les nouvelles chaînes (créées avec les transformationsLiveData
) à la vue détaillée. Pour ce faire, définissez la valeur du champ de texte du type de propriété surviewModel.displayPropertyType
et le champ de texte du texte de la valeur de 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 la propriété apparaissent sur la page d'informations, avec une mise en forme correcte.
Projet Android Studio: MarsRealEstateFinal
Expressions de liaison
- Les expressions de liaison dans les fichiers de mise en page XML permettent d'effectuer des opérations programmatiques simples, telles que des tests mathématiques ou conditionnels, sur les données liées.
- Pour référencer des classes dans votre fichier de mise en page, utilisez la balise
<import>
dans 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
@Query
dans Retrofit.
Cours Udacity:
Documentation pour les développeurs Android:
- Présentation de ViewModel
- Présentation de LiveData
- Adaptateurs de liaison
- Mises en page et expressions de liaison
- Navigation
- Premiers pas avec le composant de 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 effectuent cet atelier de programmation dans le cadre d'un cours animé par un enseignant. C'est à l'enseignant de procéder comme suit:
- Si nécessaire, rendez-les.
- Communiquez aux élèves comment rendre leurs devoirs.
- Notez les devoirs.
Les enseignants peuvent utiliser ces suggestions autant qu'ils le souhaitent, et ils n'ont pas besoin d'attribuer les devoirs de leur choix.
Si vous suivez vous-même cet atelier de programmation, n'hésitez pas à utiliser ces devoirs pour tester vos connaissances.
Répondez à ces questions
Question 1
À quoi sert la balise <import>
dans un fichier de mise en page XML ?
▢ Inclut un fichier de mise en page dans un autre.
▢ Intégrer le code Kotlin dans le fichier de mise en page
▢ Permet d'accéder aux propriétés liées aux données.
▢ Vous permettent 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 ?
▢ Ajouter la requête à la fin de l'URL de la requête
▢ Ajoutez un paramètre de requête à la fonction qui effectue la requête et annotez-le avec @Query
.
▢ Utilisez la classe Query
pour créer une requête.
▢ Utilisez la méthode addQuery()
dans le compilateur Retrofit.
Démarrez la leçon suivante :
Pour obtenir des liens vers d'autres ateliers de programmation dans ce cours, consultez la page de destination des ateliers de programmation Android Kotlin Fundamentals.