1. Avant de commencer
Introduction
Vous savez maintenant tout ce qu'il y a à connaître sur la création d'applications Android avec Compose. C'est un réel atout ! Compose est un outil très puissant qui contribue à simplifier le processus de développement. Cependant, les applications Android n'ont pas toujours été conçues avec des interfaces utilisateur déclaratives. Compose est un outil très récent dans l'histoire des applications Android. À l'origine, les interfaces utilisateur Android étaient conçues avec des vues. Par conséquent, il est fort probable que vous rencontriez des vues à mesure que vous progresserez sur votre parcours en tant que développeur Android. Dans cet atelier de programmation, vous allez découvrir les bases de la création d'applications Android avant Compose. Vous verrez donc comment utiliser le format XML, les vues, les liaisons de vue et les fragments.
Conditions préalables :
- Vous avez suivi le cours sur les principes de base d'Android dans Compose jusqu'au module 7.
Ce dont vous avez besoin
- Un ordinateur avec un accès à Internet et Android Studio
- Un appareil ou un émulateur
- Le code de démarrage de l'application Juice Tracker
Objectifs de l'atelier
Dans cet atelier de programmation, vous allez utiliser l'application Juice Tracker. Celle-ci vous permet d'effectuer le suivi de jus de fruits phares en créant une liste d'éléments détaillés. Vous ajouterez et modifierez des fragments et du code XML pour compléter l'UI et le code de démarrage. Plus précisément, vous créerez le formulaire d'ajout d'un nouveau jus de fruits, y compris l'UI et toute logique ou navigation associée. Vous obtiendrez alors une application avec une liste vide à laquelle vous pourrez ajouter vos propres jus.
2. Télécharger le code de démarrage
- Dans Android Studio, ouvrez le dossier
basic-android-kotlin-compose-training-juice-tracker
. - Ouvrez le code de l'application Juice Tracker dans Android Studio.
3. Créer une mise en page
Lorsque vous créez une application avec des Views
, vous construisez l'UI dans une mise en page. Les mises en page sont généralement déclarées au format XML. Ces fichiers XML de mise en page se trouvent dans le répertoire de ressources sous res > layout. Les mises en page contiennent les composants de l'UI. Ces composants sont appelés des View
s. La syntaxe XML se constitue de balises, d'éléments et d'attributs. Pour en savoir plus sur la syntaxe XML, consultez l'atelier de programmation Créer des mises en page XML pour Android.
Dans cette section, vous allez créer une mise en page XML pour la boîte de dialogue de saisie Type of juice (Type de jus) illustrée sur cette image.
- Créez un fichier de ressources de mise en page dans le répertoire main > res > layout. Appelez-le
fragment_entry_dialog
.
La mise en page fragment_entry_dialog.xml
contient les composants d'UI que l'application présente à l'utilisateur.
Notez que l'élément racine est ConstraintLayout
. Ce type de mise en page est un ViewGroup
qui vous permet de positionner et de dimensionner des vues de manière flexible en tenant compte des contraintes. Un ViewGroup
est un type de View
contenant d'autres View
s, appelées enfants ou View
s enfants. Les étapes suivantes couvrent ce sujet plus en détail, mais vous pouvez vous familiariser avec ConstraintLayout
dans la section Créer une UI responsive avec ConstraintLayout.
- Après avoir créé le fichier, définissez l'espace de noms de l'application dans le
ConstraintLayout
.
fragment_entry_dialog.xml
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>
- Ajoutez les consignes suivantes au
ConstraintLayout
.
fragment_entry_dialog.xml
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="16dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_middle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="16dp" />
Ces Guideline
s servent de remplissage pour les autres vues. Les directives contraignent le texte d'en-tête Type of juice (Type de jus).
- Créez un élément
TextView
. Cet élémentTextView
représente le titre du fragment de détail.
- Définissez
TextView
comme unid
deheader_title
. - Définissez
layout_width
sur0dp
. Au final, les contraintes de mise en page définiront la largeur de cet objetTextView
. Par conséquent, spécifier une largeur n'ajoute que des calculs inutiles pendant le dessin de l'interface utilisateur. Définir une largeur de0dp
évite les calculs supplémentaires. - Définissez l'attribut
TextView text
sur@string/juice_type
. - Définissez
textAppearance
sur@style/TextAppearance.MaterialComponents.Headline5
.
fragment_entry_dialog.xml
<TextView
android:id="@+id/header_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/juice_type"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5" />
Enfin, définissez les contraintes. Contrairement aux Guideline
, qui utilisent des dimensions comme contraintes, les directives elles-mêmes contraignent cet objet TextView
. Pour ce faire, vous pouvez référencer l'ID de la Guideline
qui doit contraindre la vue.
- Contraignez la partie supérieure de l'en-tête en bas de
guideline_top
.
fragment_entry_dialog.xml
<TextView
android:id="@+id/header_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/juice_type"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
app:layout_constraintTop_toBottomOf="@+id/guideline_top" />
- Contraignez la fin au début de
guideline_middle
et le début au début deguideline_left
pour terminer le positionnement deTextView
. Gardez à l'esprit que la manière dont vous contraignez une vue dépend entièrement de l'apparence de votre interface utilisateur.
fragment_entry_dialog.xml
<TextView
android:id="@+id/header_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/juice_type"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
app:layout_constraintTop_toBottomOf="@+id/guideline_top"
app:layout_constraintEnd_toStartOf="@+id/guideline_middle"
app:layout_constraintStart_toStartOf="@+id/guideline_left" />
Essayez de créer le reste de l'interface utilisateur à partir des captures d'écran. Vous trouverez le fichier fragment_entry_dialog.xml
final dans la solution.
4. Créer un fragment avec des vues
Dans Compose, vous créez des mises en page déclaratives à l'aide de Kotlin ou de Java. Vous pouvez accéder à différents "écrans" en parcourant différents composables, généralement dans la même activité. Lorsque vous créez une application avec des vues, un fragment qui héberge la mise en page XML remplace le concept de composable "écran".
Dans cette section, vous allez créer un Fragment
pour héberger la mise en page fragment_entry_dialog
et fournir des données à l'UI.
- Dans le package
juicetracker
, créez une classe intituléeEntryDialogFragment
. - Faites en sorte que
EntryDialogFragment
étendeBottomSheetDialogFragment
.
EntryDialogFragment.kt
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
class EntryDialogFragment : BottomSheetDialogFragment() {
}
DialogFragment
est un Fragment
qui affiche une boîte de dialogue flottante. BottomSheetDialogFragment
hérite de la classe DialogFragment
, mais affiche une feuille correspondant à la largeur de l'écran et épinglée en bas. Cette approche correspond à la conception illustrée précédemment.
- Recompilez le projet. Les fichiers de liaison de vue basés sur la mise en page
fragment_entry_dialog
sont alors générés automatiquement. Les liaisons de vue vous permettent d'accéder auxView
XML déclarées et d'interagir avec elles. Pour en savoir plus, consultez cette documentation. - Dans la classe
EntryDialogFragment
, implémentez la fonctiononCreateView()
. Comme son nom l'indique, cette fonction crée laView
pour ceFragment
.
EntryDialogFragment.kt
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return super.onCreateView(inflater, container, savedInstanceState)
}
La fonction onCreateView()
renvoie une View
, mais pour le moment, elle ne renvoie pas de View
utile.
- Renvoyez la
View
générée en gonflant leFragmentEntryDialogViewBinding
au lieu de renvoyersuper.onCreateView()
.
EntryDialogFragment.kt
import com.example.juicetracker.databinding.FragmentEntryDialogBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return FragmentEntryDialogBinding.inflate(inflater, container, false).root
}
- En dehors de la fonction
onCreateView()
, mais dans la classeEntryDialogFragment
, créez une instance d'EntryViewModel
. - Implémentez la fonction
onViewCreated()
.
Après avoir gonflé la liaison de vue, vous pouvez accéder aux View
s de la mise en page et les modifier. La méthode onViewCreated()
est appelée après onCreateView()
dans le cycle de vie. La méthode onViewCreated()
est recommandée pour accéder aux View
s et les modifier dans la mise en page.
- Créez une instance de la liaison de vue en appelant la méthode
bind()
surFragmentEntryDialogBinding
.
À ce stade, votre code devrait se présenter comme suit :
EntryDialogFragment.kt
import androidx.fragment.app.viewModels
import com.example.juicetracker.ui.AppViewModelProvider
import com.example.juicetracker.ui.EntryViewModel
class EntryDialogFragment : BottomSheetDialogFragment() {
private val entryViewModel by viewModels<EntryViewModel> { AppViewModelProvider.Factory }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return FragmentEntryDialogBinding.inflate(inflater, container, false).root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val binding = FragmentEntryDialogBinding.bind(view)
}
}
Vous pouvez accéder aux vues et les définir via la liaison. Par exemple, vous pouvez définir un objet TextView
via la méthode setText()
.
binding.name.setText("Apple juice")
L'interface utilisateur de la boîte de dialogue de saisie permet à l'utilisateur de créer un élément, mais vous pouvez également l'utiliser pour modifier un élément existant. Par conséquent, le fragment doit récupérer un élément sur lequel l'utilisateur a cliqué. Le composant Navigation facilite l'accès à EntryDialogFragment
et la récupération d'un élément sur lequel l'utilisateur a cliqué.
L'élément EntryDialogFragment
n'est pas encore terminé, mais ne vous inquiétez pas ! Pour l'instant, passez à la section suivante pour découvrir comment utiliser le composant de navigation dans une application avec des View
s.
5. Modifier le composant Navigation
Dans cette section, vous utiliserez le composant Navigation pour ouvrir la boîte de dialogue de saisie et pour récupérer un élément, le cas échéant.
Compose permet d'afficher différents composables rien qu'en les appelant. Toutefois, les fragments ne fonctionnent pas de la même manière. Le composant Navigation coordonne les "destinations" des fragments, ce qui permet de passer facilement d'un fragment à l'autre grâce aux vues qu'ils contiennent.
Utilisez le composant Navigation pour coordonner la navigation vers EntryDialogFragment
.
- Ouvrez le fichier
nav_graph.xml
et assurez-vous que l'onglet Design (Conception) est sélectionné. - Cliquez sur l'icône pour ajouter une destination.
- Sélectionnez la destination
EntryDialogFragment
. Cette action déclare l'élémententryDialogFragment
dans le graphique de navigation, ce qui le rend accessible pour les actions de navigation.
Vous devez lancer EntryDialogFragment
à partir de TrackerFragment
. Par conséquent, une action de navigation doit être effectuée pour accomplir cette tâche.
- Faites glisser le curseur au-dessus de
trackerFragment
. Sélectionnez le point gris et faites glisser la ligne versentryDialogFragment
. - L'affichage Projet nav_graph vous permet de déclarer des arguments pour une destination. Pour ce faire, sélectionnez la destination et cliquez sur l'icône à côté du menu déroulant Arguments. Utilisez cette fonctionnalité pour ajouter un argument
itemId
de typeLong
àentryDialogFragment
. La valeur par défaut devrait être0L
.
Notez que TrackerFragment
contient une liste d'éléments Juice
. Si vous cliquez sur l'un de ces éléments, EntryDialogFragment
s'ouvre.
- Recompilez le projet. L'argument
itemId
est désormais accessible dansEntryDialogFragment
.
6. Terminer le fragment
À l'aide des données provenant des arguments de navigation, finalisez la boîte de dialogue de saisie.
- Récupérez
navArgs()
dans la méthodeonViewCreated()
d'EntryDialogFragment
. - Récupérez
itemId
à partir denavArgs()
. - Implémentez
saveButton
pour enregistrer le jus ajouté ou modifié à l'aide deViewModel
.
À partir de l'interface utilisateur de la boîte de dialogue de saisie, indiquez via un rappel que la couleur par défaut est le rouge. Pour l'instant, transmettez cette information en tant qu'espace réservé.
Transmettez l'ID de l'élément à partir des arguments lors de l'appel de saveJuice()
.
EntryDialogFragment.kt
import androidx.navigation.fragment.navArgs
import com.example.juicetracker.data.JuiceColor
class EntryDialogFragment : BottomSheetDialogFragment() {
//...
var selectedColor: JuiceColor = JuiceColor.Red
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val binding = FragmentEntryDialogBinding.bind(view)
val args: EntryDialogFragmentArgs by navArgs()
val juiceId = args.itemId
binding.saveButton.setOnClickListener {
entryViewModel.saveJuice(
juiceId,
binding.name.text.toString(),
binding.description.text.toString(),
selectedColor.name,
binding.ratingBar.rating.toInt()
)
}
}
}
- Une fois les données enregistrées, fermez la boîte de dialogue avec la méthode
dismiss()
.
EntryDialogFragment.kt
class EntryDialogFragment : BottomSheetDialogFragment() {
//...
var selectedColor: JuiceColor = JuiceColor.Red
//...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val binding = FragmentEntryDialogBinding.bind(view)
val args: EntryDialogFragmentArgs by navArgs()
binding.saveButton.setOnClickListener {
entryViewModel.saveJuice(
juiceId,
binding.name.text.toString(),
binding.description.text.toString(),
selectedColor.name,
binding.ratingBar.rating.toInt()
)
dismiss()
}
}
}
Gardez à l'esprit que le code ci-dessus ne termine pas le EntryDialogFragment
. Vous devez encore implémenter un plusieurs éléments, tels que le remplissage des champs avec des données Juice
existantes (le cas échéant), la sélection d'une couleur à partir de colorSpinner
, l'implémentation de cancelButton
, et plus encore. Cependant, ce code n'est pas propre aux Fragment
s, et vous pouvez l'ajouter par vous-même. Essayez d'implémenter les autres fonctionnalités. En dernier recours, vous pouvez consulter le code de solution de cet atelier de programmation.
7. Ouvrir la boîte de dialogue de saisie
La dernière tâche consiste à ouvrir la boîte de dialogue de saisie à l'aide du composant Navigation. La boîte de dialogue de saisie doit s'ouvrir lorsque l'utilisateur clique sur le bouton d'action flottant. Elle doit également lancer et transmettre l'ID correspondant lorsque l'utilisateur clique sur un élément.
- Dans le
onClickListener()
du bouton d'action flottant, appeleznavigate()
au niveau du contrôleur de navigation.
TrackerFragment.kt
import androidx.navigation.findNavController
//...
binding.fab.setOnClickListener { fabView ->
fabView.findNavController().navigate(
)
}
//...
- Dans la fonction de navigation, transmettez l'action permettant de passer de l'outil de suivi à la boîte de dialogue de saisie.
TrackerFragment.kt
//...
binding.fab.setOnClickListener { fabView ->
fabView.findNavController().navigate(
TrackerFragmentDirections.actionTrackerFragmentToEntryDialogFragment()
)
}
//...
- Répétez cette action dans le corps du lambda pour la méthode
onEdit()
dansJuiceListAdapter
, mais cette fois, transmettez leid
deJuice
.
TrackerFragment.kt
//...
onEdit = { drink ->
findNavController().navigate(
TrackerFragmentDirections.actionTrackerFragmentToEntryDialogFragment(drink.id)
)
},
//...
8. Télécharger le code de solution
Pour télécharger le code de cet atelier de programmation terminé, utilisez les commandes Git suivantes :
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git $ cd basic-android-kotlin-compose-training-juice-tracker $ git checkout views
Vous pouvez également télécharger le dépôt sous forme de fichier ZIP, le décompresser et l'ouvrir dans Android Studio.
Si vous souhaitez voir le code de solution, affichez-le sur GitHub.