Android Kotlin Fundamentals 08.3 Filtering and detail views with Internet data

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.

  1. Ouvrez l'application MarsRealEstate à partir du dernier atelier de programmation. (Vous pouvez télécharger MarsRealEstateGrid si vous ne disposez pas de cette application.)
  2. Ouvrez network/MarsProperty.kt. Ajoutez un corps à la définition de la classe MarsProperty, ainsi qu'un getter personnalisé pour isRental qui renvoie true 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.

  1. 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 dans RecyclerView. Actuellement, le fichier ne contient que l'élément <ImageView> de l'image de la propriété.
  2. Dans l'élément <data>, ajoutez un élément <import> pour la classe View. 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 constantes View.GONE et View.VISIBLE. Vous devez donc accéder à la classe View.
<import type="android.view.View"/>
  1. 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>
  1. Pour ImageView, remplacez l'attribut android:layout_height par match_parent pour remplir le nouveau parent FrameLayout.
android:layout_height="match_parent"
  1. Ajoutez un deuxième élément <ImageView> juste en dessous du premier, dans la section FrameLayout. 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 dans res/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"/>
  1. Ajoutez l'attribut android:visibility à la vue d'image mars_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>
  1. 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.

  1. Ouvrez network/MarsApiService.kt. Juste en dessous des importations, créez un enum 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") }
  1. 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.

    Importez retrofit2.http.Query lorsque vous y êtes invité.

    L'annotation @Query indique à la méthode getProperties() (et donc à Retrofit) d'effectuer la requête de service Web avec l'option de filtre. Chaque fois que getProperties() 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.

  1. 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. Ajoutez MarsApiFilter (énumération des valeurs de filtre possibles) comme paramètre à l'appel de getMarsRealEstateProperties().

    Importez com.example.android.marsrealestate.network.MarsApiFilter lorsque vous le demandez.
private fun getMarsRealEstateProperties(filter: MarsApiFilter) {
  1. 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)
  1. Dans le bloc init {}, transmettez MarsApiFilter.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)
}
  1. À la fin de la classe, ajoutez une méthode updateFilter() qui utilise un argument MarsApiFilter et appelle getMarsRealEstateProperties() 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.

  1. 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>
  1. Ouvrez overview/OverviewFragment.kt. À la fin de la classe, implémentez la méthode onOptionsItemSelected() pour gérer les sélections d'éléments de menu.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
} 
  1. Dans onOptionsItemSelected(), appelez la méthode updateFilter() sur le modèle de vue avec le filtre approprié. Utilisez un bloc when {} Kotlin pour passer d'une option à l'autre. Utilisez MarsApiFilter.SHOW_ALL pour la valeur de filtre par défaut. Renvoyez true, car vous avez géré l'élément de menu. Importez MarsApiFilter (com.example.android.marsrealestate.network.MarsApiFilter) à la demande. La méthode onOptionsItemSelected() 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
}
  1. 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.
  2. 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.
  3. 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.

  1. Ouvrez detail/DetailViewModel.kt. Tout comme les fichiers Kotlin liés au réseau sont contenus dans le dossier network et les fichiers de présentation dans overview, le dossier detail contient les fichiers associés à la vue détaillée. Notez que la classe DetailViewModel (vide actuellement) utilise un marsProperty comme paramètre dans le constructeur.
class DetailViewModel( marsProperty: MarsProperty,
                     app: Application) : AndroidViewModel(app) {
}
  1. 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'un MutableLiveData pour contenir le MarsProperty lui-même, puis exposez une propriété LiveData publique immuable.

    Importez androidx.lifecycle.LiveData et importez androidx.lifecycle.MutableLiveData à la demande.
private val _selectedProperty = MutableLiveData<MarsProperty>()
val selectedProperty: LiveData<MarsProperty>
   get() = _selectedProperty
  1. Créez un bloc init {} et définissez la valeur de la propriété Mars sélectionnée avec l'objet MarsProperty du constructeur.
    init {
        _selectedProperty.value = marsProperty
    }
  1. 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 un ImageView pour la grande photo, un TextView pour le type d'établissement (location ou vente) et un TextView pour le prix. Notez que la mise en page de la contrainte est encapsulée avec une ScrollView afin qu'elle défile automatiquement si l'affichage devient trop volumineux, par exemple lorsque l'utilisateur l'affiche en mode Paysage.
  2. 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>
  1. Ajoutez l'attribut app:imageUrl à l'élément ImageView. Définissez-la sur imgSrcUrl à 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 attributs app: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é.

  1. Ouvrez overview/OverviewViewModel.kt. Ajoutez une propriété _navigateToSelectedProperty MutableLiveData et exposez-la avec une propriété LiveData immuable.

    Lorsque ce paramètre LiveData 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
  1. 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
}
  1. 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

  1. Ouvrez overview/PhotoGridAdapter.kt. À la fin de la classe, créez une classe OnClickListener personnalisée qui utilise un lambda avec un paramètre marsProperty. Dans la classe, définissez une fonction onClick() définie sur le paramètre lambda.
class OnClickListener(val clickListener: (marsProperty:MarsProperty) -> Unit) {
     fun onClick(marsProperty:MarsProperty) = clickListener(marsProperty)
}
  1. 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) {
  1. Rendez une photo cliquable en ajoutant le onClickListener à l'élément de la grille dans la méthode onBindviewHolder(). 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)
}
  1. Ouvrez overview/OverviewFragment.kt. Dans la méthode onCreateView(), remplacez la ligne qui initialise la propriété binding.photosGrid.adapter par la ligne affichée ci-dessous.

    Ce code ajoute l'objet PhotoGridAdapter.onClickListener au constructeur PhotoGridAdapter et appelle viewModel.displayPropertyDetails() avec l'objet MarsProperty transmis. Cela déclenche le LiveData 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.

  1. Ouvrez res/navigation/nav_graph.xml. Cliquez sur l'onglet Text (Texte) pour afficher le code XML du graphique de navigation.
  2. Dans l'élément <fragment> du fragment détaillé, ajoutez l'élément <argument> ci-dessous. Cet argument, appelé selectedProperty, est de type MarsProperty.
<argument
   android:name="selectedProperty"
   app:argType="com.example.android.marsrealestate.network.MarsProperty"
   />
  1. Compilez l'application. La navigation génère une erreur, car le MarsProperty n'est pas parcelable. L'interface Parcelable 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'objet MarsProperty soient transmises au fragment de détails via Safe Args, MarsProperty doit implémenter l'interface Parcelable. La bonne nouvelle, c'est que Kotlin propose un raccourci facile pour implémenter cette interface.
  2. Ouvrez network/MarsProperty.kt. Ajoutez l'annotation @Parcelize à la définition de classe.

    Importez kotlinx.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'interface Parcelable de cette classe. Aucune action n'est requise de votre part.
@Parcelize
data class MarsProperty (
  1. Modifiez la définition de la classe MarsProperty pour étendre Parcelable.

    Importez android.os.Parcelable lorsque vous l'avez demandé.

    La définition de la classe MarsProperty 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".

  1. Ouvrez overview/OverviewFragment.kt. Dans onCreateView(), sous les lignes qui initialisent l'adaptateur de la grille de photos, ajoutez les lignes ci-dessous pour observer le navigatedToSelectedProperty du modèle de vue d'ensemble.

    Importez androidx.lifecycle.Observer et importez androidx.navigation.fragment.findNavController à la demande.

    L'observateur teste si MarsProperty (le it du lambda) n'est pas nul et, le cas échéant, il obtient le contrôleur de navigation du fragment avec findNavController(). Appelez displayPropertyDetailsComplete() pour indiquer au modèle de vue de réinitialiser la valeur LiveData. Ainsi, vous ne déclencherez pas accidentellement une nouvelle navigation lorsque l'application reviendra au OverviewFragment.
viewModel.navigateToSelectedProperty.observe(this, Observer {
   if ( null != it ) {   
      this.findNavController().navigate(
              OverviewFragmentDirections.actionShowDetail(it))             
      viewModel.displayPropertyDetailsComplete()
   }
})
  1. Ouvrez detail/DetailFragment.kt. Ajoutez cette ligne juste en dessous de l'appel de setLifecycleOwner() dans la méthode onCreateView(). Cette ligne obtient l'objet MarsProperty sélectionné à partir des arguments sécurisés.

    Observez l'utilisation de l'opérateur d'assertion "non-null" (!!). Si l'instruction selectedProperty 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
  1. Ajoutez ensuite cette ligne pour obtenir un nouveau DetailViewModelFactory. Vous allez utiliser DetailViewModelFactory pour obtenir une instance de DetailViewModel. L'application de départ inclut une implémentation de DetailViewModelFactory. Il vous suffit donc de l'initialiser ici.
val viewModelFactory = DetailViewModelFactory(marsProperty, application)
  1. 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)
  1. 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.

  1. 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 ressource display_price_monthly_rental ou la ressource display_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>
  1. Ouvrez detail/DetailViewModel.kt. En bas de la classe, ajoutez le code indiqué ci-dessous.

    Importez androidx.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 commutateur when {} Kotlin. Ces deux chaînes ont besoin d'un numéro à la fin. Vous concaténez donc property.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)
}
  1. Importez la classe R générée pour accéder aux ressources de chaîne du projet.
import com.example.android.marsrealestate.R
  1. 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
                   }))
}
  1. 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 transformations LiveData) à la vue détaillée. Pour ce faire, définissez la valeur du champ de texte du type de propriété sur viewModel.displayPropertyType et le champ de texte du texte de la valeur de prix sur viewModel.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" />
  1. 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:

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 : 9.1: Dépôt.

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.