Ces travaux pratiques se base sur le cours de base pour les développeurs Android fourni par Google afin de les préparer pour le test de certification Associate Android Developer. Vous obtiendrez le plus de valeur de ce TP si vous travaillez successivement dans les codelabs.

Introduction

Le système d'exploitation Android fournit une base solide pour la création d'applications qui fonctionnent bien sur un large éventail d'appareils et de facteurs de forme. Cependant, des problèmes tels que des cycles de vie complexes et l'absence d'une architecture d'application recommandée rendent difficile l'écriture d'applications robustes. Les composants d'architecture Android fournissent des bibliothèques pour des tâches courantes telles que la gestion du cycle de vie et la persistance des données, afin de faciliter la mise en œuvre de l'architecture recommandée.

Les composants d'architecture vous aident à structurer votre application d'une manière robuste, testable et maintenable avec moins de code boilerplate.

Quels sont les composants d'architecture recommandés ?

Lorsqu'il s'agit d'architecture, il est utile de voir d'abord la vue d'ensemble. Pour introduire la terminologie, voici un bref aperçu des composants d'architecture et de la manière dont ils fonctionnent ensemble. Chaque composant est expliqué plus en détail lorsque vous l'utilisez dans ce cours pratique.

Le diagramme ci-dessous montre une forme de base de l'architecture recommandée pour les applications qui utilisent les composants d'architecture. L'architecture se compose d'un contrôleur d'interface utilisateur, d'un ViewModel qui fournit LiveData, d'un référentiel et d'une base de données Room. La base de données Room est basée sur une base de données SQLite et accessible via un objet d'accès aux données (DAO). Chaque composant est décrit brièvement ci-dessous. Vous implémentez les composants dans cette pratique.

Parce que tous les composants interagissent, vous rencontrerez des références à ces composants tout au long de ce guide pratique, voici donc une brève explication de chacun d'eux.

Entity (Entité) : Dans le contexte des composants d'architecture, l'entité est une classe annotée qui décrit une table de base de données.

Base de données

SQLite : Sur l'appareil, les données sont stockées dans une base de données SQLite. La bibliothèque de persistance Room crée et maintient cette base de données pour vous.

Base de données Room : Simplifie le travail de base de données et sert de point d'accès à la base de données SQLite sous-jacente (cache SQLiteOpenHelper). La base de données Room utilise le DAO pour émettre des requêtes vers les données SQLite.

DAO : Acronyme de Data Access Object, qui signifie objet d'accès aux données. Il s'agit d'un mappage de requêtes SQL en fonction. Auparavant, vous deviez définir ces requêtes dans une classe d'assistance. Lorsque vous utilisez un DAO, votre code appelle les fonctions, et les composants s'occupent du reste.

Repository : Une classe que vous créez pour gérer plusieurs sources de données. En plus d'une base de données Room, le Repository peut gérer des sources de données distantes telles qu'un serveur Web.

ViewModel: Fournit des données à l'interface utilisateur (UI) et agit comme un centre de communication entre le référentiel (Repository) et l'UI. Masque l'arrière-plan à l'UI. Les instances de ViewModel survivent aux changements de configuration de l'appareil.

LiveData: Une classe de conteneur de données qui suit le modèle d'observateur, ce qui signifie qu'elle peut être observée. Contient et met toujours en cache la dernière version des données. Notifie ses observateurs lorsque les données ont changé. En général, les composants de l'interface utilisateur observent les données pertinentes. LiveData est sensible au cycle de vie, elle gère donc automatiquement l'arrêt et la reprise de l'observation en fonction de l'état de l'activité ou du fragment qui l'observe.

What you should already know

Vous devez être familiarisé avec Java, les concepts de conception orientée objet et les fondamentaux du développement Android. En particulier :

What you'll learn

What you'll do

Dans ce cours pratique, vous allez créer une application qui utilise les composants d'architecture Android. L'application, appelée RoomWordsSample, stocke une liste de mots dans une base de données Room et affiche la liste dans un RecyclerView. L'application RoomWordsSample est basique, mais suffisamment complète pour que vous puissiez l'utiliser comme modèle de départ.

L'application RoomWordsSample fait ce qui suit :

Aperçu de l'architecture de RoomWordSample

Le diagramme suivant reprend le diagramme d'ensemble de l'introduction et montre toutes les pièces de l'application RoomWordsSample. Chacune des boîtes englobantes (à l'exception de la base de données SQLite) représente un composant de l'application qui peut être réutilisé par d'autres applications

1.1 Créer une application avec une seule activité

  1. Ouvrez Android Studio et cliquez sur New Project.
  2. Dans la fenêtre New Project, choisissez Basic Views Activity et cliquez sur Next.
  3. Sur l'écran suivant, nommez l'application RoomWordSample et cliquez sur Finish.
  4. Ouvrez themes.xml, changez le parent de du thème Base.Theme.RoomWordSample de l'application par Theme.Material3.Light.NoActionBar et supprimez le fichier themes.xml (night).
    C'est juste pour avoir la même interface utilisateur, parce que le mode sombre peut être activé sur vos émulateurs ou téléphones.
  5. Supprimez le répertoire "navigation" sous les ressources (res).
  6. Supprimez toutes les mises en page et classes Java des fragments.
    FirstFragment.java, SecondFragment.java, fragment_first.xml et fragment_second.xml.
  7. Supprimez la balise fragment de "content_main.xml" .
  8. Supprimez les dépendances de la bibliothèque de navigation et synchronisez le projet après la suppression.
    implementation libs.navigation.fragment
    implementation libs.navigation.ui
  1. Nettoyez "MainActivity.java" (les imports, methode de navigation onSupportNavigateUp() et tous les variables en rouge en relation avec la bibliothèque de navigation...)
  2. Exécutez votre application.

1.2 Mettre à jour les fichiers Gradle

Ensuite, vous devez ajouter les bibliothèques de composants à vos fichiers Gradle.

  1. Dans Android Studio, cliquez sur l'onglet Projets et développez le dossier Gradle Scripts.
  2. Ouvrir libs.versions.toml.
  3. À la fin de la section [versions], ajoutez les deux variables suivantes qui définissent les versions des bibliothèques "room" et "lifecycle" :
room = "2.6.1"
lifecycle = "2.8.6"
  1. À la fin de la section [libraries], ajoutez la définition des packages des deux bibliothèques ainsi que leurs versions respectives :
# Composant de base de données Room
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
room-testing = { group = "androidx.room", name = "room-testing", version.ref = "room" }
# Composants de cycle de vie
lifecycle-viewmodel = { group = "androidx.lifecycle", name = "lifecycle-viewmodel", version.ref = "lifecycle" }
lifecycle-livedata = { group = "androidx.lifecycle", name = "lifecycle-livedata", version.ref = "lifecycle" }
lifecycle-common-java8 = { group = "androidx.lifecycle", name = "lifecycle-common-java8", version.ref = "lifecycle" }
  1. Ouvrir build.gradle (Module: app).
  2. Ajoutez le code suivant en bas du bloc des dépendances dependencies (mais toujours à l'intérieur du bloc).
// Dépendances pour travailler avec les composants d'architecture
// Vous devrez probablement mettre à jour les numéros de version dans libs.versions.toml

// Composant de base de données Room
implementation libs.room.runtime
annotationProcessor libs.room.compiler
testImplementation libs.room.testing

// Composants de cycle de vie
implementation libs.lifecycle.viewmodel
implementation libs.lifecycle.livedata
implementation libs.lifecycle.common.java8
  1. Synchronisez votre projet

Les données de cette application sont des mots, et vous aurez besoin d'une table simple pour stocker ces valeurs:

Les composants d'architecture vous permettent d'en créer un via une entité (Entity). Faisons-le maintenant.

2.1 Créer la classe Word

  1. Effectuez un clic droit sur app > java > com.example.enetcom.roomwordsample, puis sélectionnez New > Package.
  2. Saisissez model comme dernière partie du nom du package.
  3. Effectuez un clic droit sur le package model, puis sélectionnez New > Java Class.
  4. Saisissez Word comme nom de classe. Cette classe décrira l'entité (qui représente la table SQLite) pour vos mots. Chaque propriété de la classe représente une colonne de la table. Room utilisera ces propriétés pour créer la table et instancier des objets à partir de lignes de la base de données. Voici le code :
public class Word {

   private String mWord;

   public Word(@NonNull String word) {this.mWord = word;}

   public String getWord(){return this.mWord;}
}

2.2 Annoter la classe Word

Pour que la classe Word ait un sens pour une base de données Room, vous devez l'annoter. Les annotations identifient la manière dont chaque partie de cette classe se rapporte à une entrée dans la base de données. Room utilise ces informations pour générer du code.

Mettez à jour votre classe Word avec des annotations comme indiqué dans ce code :

@Entity(tableName = "word_table")
public class Word {

   @PrimaryKey
   @NonNull
   @ColumnInfo(name = "word")
   private String mWord;

   public Word(@NonNull String word) {this.mWord = word;}

   public String getWord(){return this.mWord;}
}

Voyons ce que font ces annotations:

Vous trouver une liste complète des annotations dans la référence du package Room.

3.1 Qu'est-ce qu'une DAO ?

Un DAO (data access object) valide votre SQL à la compilation et l'associe à une méthode. Dans votre DAO Room, vous utilisez des annotations pratiques, comme @Insert, pour représenter les opérations de base de données les plus courantes ! Room utilise le DAO pour créer une API propre pour votre code.

Le DAO doit être une interface ou une classe abstraite. Par défaut, toutes les requêtes doivent être exécutées sur un thread distinct.

3.2 Créez une classe DAO

Écrivons un DAO qui fournit des requêtes pour :

  1. Effectuez un clic droit sur app > java > com.example.enetcom.roomwordsample, puis sélectionnez New > Package.
  2. Saisissez data.db comme dernière partie du nom du package.
  3. Effectuez un clic droit sur le package data.db, puis sélectionnez New > Java Class.
  4. Saisissez WordDao comme nom et sélectionnez interface.
  5. Copy and paste the following code into WordDao and fix the imports as necessary to make it compile:
@Dao
public interface WordDao {

   // Permettre l'insertion du même mot plusieurs fois en passant
   // une stratégie de résolution de conflits.
   @Insert(onConflict = OnConflictStrategy.IGNORE)
   void insert(Word word);

   @Query("DELETE FROM word_table")
   void deleteAll();

   @Query("SELECT * FROM word_table ORDER BY word ASC")
   List<Word> getAlphabetizedWords();
}

Expliquons-le point par point:

Lorsque les données changent, vous souhaitez généralement effectuer une action, telle que l'affichage des données mises à jour dans l'interface utilisateur. Cela signifie que vous devez observer les données afin de pouvoir réagir lorsqu'elles changent.

Selon la façon dont les données sont stockées, cela peut être délicat. L'observation des modifications apportées aux données dans plusieurs composants de votre application peut créer des chemins de dépendance explicites et rigides entre les composants. Cela rend les tests et le débogage difficiles, entre autres.

LiveData, une classe de bibliothèque de cycle de vie pour l'observation des données, résout ce problème. Utilisez une valeur de retour de type LiveData dans la description de votre méthode, et Room générera tout le code nécessaire pour mettre à jour LiveData lorsque la base de données est mise à jour.

4.1 Retourner LiveData dans WordDao

  @Query("SELECT * from word_table ORDER BY word ASC")
   LiveData<List<Word>> getAlphabetizedWords();

Plus tard dans ce codelab, vous suivez les changements de données via un observateur (observer) dans MainActivity.

5.1 Qu'est-ce qu'une base de données Room ?

5.2 Implémenter la base de données Room

Votre classe de base de données Room doit être abstraite et hériter de RoomDatabase. En général, vous n'avez besoin que d'une seule instance d'une base de données Room pour toute l'application.

Créons-en un maintenant. Créez un fichier de classe appelé WordRoomDatabase sous le package db et ajoutez-y le code suivant :

@Database(entities = {Word.class}, version = 1, exportSchema = false)
public abstract class WordRoomDatabase extends RoomDatabase {

   public abstract WordDao wordDao();

   private static volatile WordRoomDatabase INSTANCE;
   private static final int NUMBER_OF_THREADS = 4;
   public static final ExecutorService databaseWriteExecutor =
        Executors.newFixedThreadPool(NUMBER_OF_THREADS);

   public static WordRoomDatabase getDatabase(final Context context) {
        if (INSTANCE == null) {
            synchronized (WordRoomDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            WordRoomDatabase.class, "word_database")
                            .fallbackToDestructiveMigration()
                            .build();
                }
            }
        }
        return INSTANCE;
    }
}

Parcourons le code ensemble:

6.1 Qu'est-ce qu'une classe Repository ?

Une classe Repository est une abstraction qui permet d'accéder à plusieurs sources de données. La classe Repository ne fait pas partie des bibliothèques Architecture Components, mais elle est suggérée comme bonne pratique pour la séparation du code et l'architecture. Une classe Repository fournit une API propre pour l'accès aux données au reste de l'application.

6.2 Pourquoi utiliser une classe Repository ?

Un référentiel (Repository) gère les requêtes et vous permet d'utiliser plusieurs bases de données. Dans l'exemple le plus courant, le référentiel implémente la logique permettant de décider s'il faut récupérer les données d'un réseau ou utiliser les résultats mis en cache dans une base de données locale.

6.3 Implémenter la classe Repository

  1. Créer une classe publique appelée WordRepository sous le package com.example.enetcom.roomwordsample
  2. Effectuez un clic droit sur la classe WordRepository, puis sélectionnez Refactor > move class pour la déplacer sous le package data
  3. Ajoutez le code suivant
public class WordRepository {

    private final WordDao mWordDao;
    private final LiveData<List<Word>> mAllWords;

    public WordRepository(Application application) {
        WordRoomDatabase db = WordRoomDatabase.getDatabase(application);
        mWordDao = db.wordDao();
        mAllWords = mWordDao.getAlphabetizedWords();
    }

    // Room exécute toutes les requêtes sur un thread distinct.
    // Les données LiveData observées avertiront l'observateur lorsque les données auront changé.
    public LiveData<List<Word>> getAllWords() {
        return mAllWords;
    }

    // Vous devez appeler cela sur un thread non-UI ou votre application lancera une exception.
    // Room garantit que vous n'effectuez aucune opération longue sur le thread principal, bloquant l'interface utilisateur.
    public void insert(Word word) {
        WordRoomDatabase.databaseWriteExecutor.execute(() -> mWordDao.insert(word));
    }
}

7.1 Qu'est-ce qu'un ViewModel ?

Le rôle de la ViewModel est de fournir des données à l'interface utilisateur et de survivre aux changements de configuration. Une ViewModel agit comme un centre de communication entre le Repository et l'interface utilisateur. Vous pouvez également utiliser une ViewModel pour partager des données entre des fragments. La ViewModel est une partie de la bibliothèque Lifecycle.

Pour une introduction à ce sujet, consultez ViewModel Overview ou le billet de blog ViewModels: A Simple Example.

7.2 Pourquoi utiliser un ViewModel ?

Un ViewModel conserve les données de l'interface utilisateur de votre application de manière consciente du cycle de vie, ce qui leur permet de survivre aux changements de configuration. Séparer les données de l'interface utilisateur de votre application de vos classes Activity et Fragment vous permet de mieux suivre le principe de responsabilité unique : vos activités et fragments sont responsables de l'affichage des données à l'écran, tandis que votre ViewModel peut s'occuper de conserver et de traiter toutes les données nécessaires à l'interface utilisateur.

Dans le ViewModel, utilisez LiveData pour les données modifiables que l'interface utilisateur utilisera ou affichera. L'utilisation de LiveData présente plusieurs avantages :

7.3 Implémenter le ViewModel

Créez un fichier de classe pour WordViewModel sous le package com.example.enetcom.roomwordsample et ajoutez-y ce code :

public class WordViewModel extends AndroidViewModel {

    private final WordRepository mRepository;

    private final LiveData<List<Word>> mAllWords;

    public WordViewModel (Application application) {
        super(application);
        mRepository = new WordRepository(application);
        mAllWords = mRepository.getAllWords();
    }

    LiveData<List<Word>> getAllWords() { return mAllWords; }

    public void insert(Word word) { mRepository.insert(word); }
}

Ici, nous avons:

Ensuite, vous devez ajouter la mise en page XML pour la liste et les éléments.

Ce codelab suppose que vous êtes familier avec la création de mises en page en XML, nous vous fournissons donc simplement le code.

8.1 Ajouter des styles

Ajouter un style pour les vues de texte dans le fichier values/styles.xml

<style name="word_title">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_marginBottom">8dp</item>
        <item name="android:padding">16dp</item>
        <item name="android:layout_gravity">center</item>
        <item name="android:background">@android:color/holo_orange_light</item>
        <item name="android:textAppearance">@android:style/TextAppearance.Large</item>
</style>

8.2 Ajouter la mise en page (layout) d'un élément

Ajoutez une mise en page layout/recyclerview_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/textView"
        style="@style/word_title" />
</LinearLayout>

8.3 Ajouter le RecyclerView

Ajoutez un élément RecyclerView au fichier layout/content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:padding="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:listitem="@layout/recyclerview_item"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        android:scrollbars="vertical"/>

</androidx.constraintlayout.widget.ConstraintLayout>

8.4 Changer l'icône du FAB

L'apparence du bouton d'action flottant (FAB) doit correspondre à l'action disponible, nous allons donc remplacer l'icône par un symbole +.

Tout d'abord, nous devons ajouter un nouvel asset vectoriel.First, we need to add a new Vector Asset:

  1. Selectionnez File > New > Vector Asset.
  2. Cliquez sur l'icône du robot Android dans Icon.
  3. Recherchez "add" et sélectionnez l'élément "+". Cliquez sur OK.
  4. Après cela, cliquez sur Next.
  5. Confirmer le chemin de l'icône comme main > drawable et cliquer sur Finish pour ajouter l'asset.
  6. Toujours dans layout/activity_main.xml, mettez à jour le FAB pour inclure le nouveau icône :
<com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_marginEnd="@dimen/fab_margin"
        android:layout_marginBottom="16dp"
        app:srcCompat="@drawable/baseline_add_24" />

Vous allez afficher les données dans un RecyclerView, ce qui est un peu plus agréable que de simplement les jeter dans un TextView. Ce codelab suppose que vous savez comment fonctionnent RecyclerView, RecyclerView.ViewHolder et RecyclerView.Adapter.

9.1 Create the WordListAdapter class

Créez une classe WordListAdapter qui étend RecyclerView.Adapter sous le package "adapter" (com.example.enetcom.roomwordsample.adapter). Voici le code :

public class WordListAdapter extends RecyclerView.Adapter<WordListAdapter.WordViewHolder> {

    private List<Word> mWords; // Copie en cache des mots

    public WordListAdapter() {}

    @NonNull
    @Override
    public WordViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item, parent, false);
        return new WordViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull WordViewHolder holder, int position) {
        if (mWords != null) {
            Word current = mWords.get(position);
            holder.wordItemView.setText(current.getWord());
        } else {
            // Couvre le cas où les données ne sont pas encore prêtes.
            holder.wordItemView.setText("No Word");
        }
    }

    public void setWords(List<Word> words){
        mWords = words;
        notifyDataSetChanged();
    }

    // getItemCount() est appelé plusieurs fois, et lorsqu'il est appelé pour la première fois,
    // mWords n'a pas été mis à jour (cela signifie qu'initialement, il est nul, et nous ne pouvons pas retourner nul).
    @Override
    public int getItemCount() {
        if (mWords != null)
            return mWords.size();
        else return 0;
    }

    class WordViewHolder extends RecyclerView.ViewHolder {
        private final TextView wordItemView;

        private WordViewHolder(View itemView) {
            super(itemView);
            wordItemView = itemView.findViewById(R.id.textView);
        }
    }
}

9.2 Ajouter RecyclerView à l'activité principale

  1. Allez dans activity_main.xml et localisez la balise include, puis ajoutez-lui content_main comme un ID:
<include layout="@layout/content_main" android:id="@+id/content_main" />
  1. Ajoutez le RecyclerView dans la méthode onCreate() de MainActivity. (après l'appel vers setContentView(binding.getRoot());)
WordListAdapter mAdapter = new WordListAdapter();
binding.contentMain.recyclerview.setAdapter(mAdapter);
binding.contentMain.recyclerview.setHasFixedSize(true);
  1. Exécutez votre application pour vous assurer que tout fonctionne. Il n'y a pas d'éléments car vous n'avez pas encore connecté les données.


Il n'y a pas de données dans la base de données. Vous pouvez ajouter des données de deux manières : en ajoutant des données lors de l'ouverture de la base de données ou en ajoutant une activité pour ajouter des mots.

Pour supprimer tout le contenu et remplir la base de données lors de l'installation de l'application, vous créez un RoomDatabase.Callback et remplacez la méthode onCreate().

Voici le code pour créer le rappel dans la classe WordRoomDatabase. Comme vous ne pouvez pas effectuer d'opérations sur la base de données Room sur le thread de l'interface utilisateur, onCreate() utilise l'executor databaseWriteExecutor défini précédemment pour exécuter un lambda sur un thread d'arrière-plan. Le lambda supprime le contenu de la base de données, puis la remplit avec les deux mots "Hello" et "World". N'hésitez pas à ajouter d'autres mots !

private static RoomDatabase.Callback sRoomDatabaseCallback = new RoomDatabase.Callback() {
        @Override
        public void onOpen(@NonNull SupportSQLiteDatabase db) {
            super.onOpen(db);

            // Si vous souhaitez conserver les données au redémarrage de l'application
            // commentez le bloc suivant
            databaseWriteExecutor.execute(() -> {
                // Remplir la base de données en arrière-plan
                // Si vous voulez commencer avec plus de mots, il suffit de les ajouter.
                WordDao dao = INSTANCE.wordDao();
                dao.deleteAll();

                Word word = new Word("Hello");
                dao.insert(word);
                word = new Word("World");
                dao.insert(word);
            });
        }
    };

Ensuite, ajoutez le callback à la séquence de construction de la base de données juste avant d'appeler .build() sur Room.databaseBuilder()

.addCallback(sRoomDatabaseCallback)

Maintenant que vous avez créé la méthode pour remplir la base de données avec l'ensemble initial de mots, l'étape suivante consiste à ajouter le code pour afficher ces mots dans le RecyclerView.

Pour afficher le contenu actuel de la base de données, ajoutez un observateur qui observe les LiveData dans le ViewModel.

Chaque fois que les données changent, la méthode de rappel onChanged() est invoquée. Cette méthode appelle la méthode setWords() de l'adaptateur pour mettre à jour les données mises en cache de l'adaptateur et rafraîchir la liste affichée.

  1. Dans MainActivity, créez une variable membre pour le ViewModel.
private WordViewModel mWordViewModel;

Utilisez ViewModelProvider pour associer votre ViewModel à votre activité.

Lorsque votre activité est lancée pour la première fois, ViewModelProviders crée le ViewModel. Lorsque l'activité est détruite, par exemple lors d'un changement de configuration, le ViewModel persiste. Lorsque l'activité est recréée, ViewModelProviders renvoie le ViewModel existant. Pour plus d'informations, consultez ViewModel.

  1. Dans onCreate(), sous le bloc de code RecyclerView, obtenez un ViewModel à partir du ViewModelProvider :
mWordViewModel = new ViewModelProvider(this).get(WordViewModel.class);
  1. Également dans onCreate(), ajoutez un observateur pour le LiveData retourné par getAlphabetizedWords(). La méthode onChanged() est déclenchée lorsque les données observées changent et que l'activité est au premier plan.
mWordViewModel = new ViewModelProvider(this).get(WordViewModel.class);
        mWordViewModel.getAllWords().observe(this, words -> {
            // Mettre à jour la copie en cache des mots dans l'adaptateur.
            mAdapter.setWords(words);
        });
  1. Exécutez l'application. L'ensemble initial de mots "Hello" et " World" apparaît dans le RecyclerView.

Vous allez maintenant ajouter une activité qui permet à l'utilisateur d'utiliser le FAB pour saisir de nouveaux mots.

12.1 Créer l'activité NewWordActivity

  1. Ajoutez ces ressources de chaîne dans values/strings.xml:
<string name="hint_word">Word...</string>
<string name="button_save">Save</string>
<string name="empty_not_saved">Word not saved because it is empty.</string>
  1. Ajoutez un style aux boutons dans values/styles.xml:
<style name="button_style" parent="Widget.Material3.Button">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:textAppearance">@android:style/TextAppearance.Large</item>
        <item name="android:layout_marginTop">16dp</item>
</style>
  1. Ajoutez un style, pour le thème de la nouvelle activitée que vous allez la créer par la suite, dans values/themes.xml:
<style name="Theme.RoomWordSample.WithActionBar" parent="Base.Theme.RoomWordSample" >
        <item name="windowActionBar">true</item>
        <item name="windowNoTitle">false</item>
        <item name="toolbarStyle">@style/Widget.Material3.Toolbar.Surface</item>
</style>
  1. Utilisez le modèle Empty Views Activity pour créer une nouvelle activité, NewWordActivity. Vérifiez que l'activité a été ajoutée au manifeste Android.
<activity
            android:name=".NewWordActivity"
            android:exported="false" />
  1. Appliquez le nouveau thème Theme.RoomWordSample.WithActionBar à la nouvelle activité et spécifiez MainActivity comme son parent.
<activity
            android:name=".NewWordActivity"
            android:theme="@style/Theme.RoomWordSample.WithActionBar"
            android:parentActivityName=".MainActivity"
            android:exported="false" />
  1. Mettez à jour le fichier activity_new_word.xml dans le dossier layout avec le code suivant :
<LinearLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="32dp"
    tools:context=".NewWordActivity">

    <EditText
        android:id="@+id/edit_word"
        style="@style/word_title"
        android:hint="@string/hint_word"
        android:inputType="textAutoComplete" />

    <Button
        android:id="@+id/button_save"
        android:text="@string/button_save"
        style="@style/button_style"/>

</LinearLayout>
  1. Implémentez la classe NewWordActivity sous le même package que MainActivity. L'objectif est que lorsque l'utilisateur appuie sur le bouton Save, le nouveau mot soit placé dans un Intent pour être renvoyé à l'activité principale. Voici le code de l'activité NewWordActivity :
public class NewWordActivity extends AppCompatActivity {
   public static final String EXTRA_REPLY = "unique.key.for.REPLY";

   private EditText mEditWordView;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_new_word);

       mEditWordView = findViewById(R.id.edit_word);

       final Button button = findViewById(R.id.button_save);
       button.setOnClickListener(new View.OnClickListener() {
           public void onClick(View view) {
               Intent replyIntent = new Intent();
               if (TextUtils.isEmpty(mEditWordView.getText())) {
                   setResult(RESULT_CANCELED, replyIntent);
               } else {
                   String word = mEditWordView.getText().toString();
                   replyIntent.putExtra(EXTRA_REPLY, word);
                   setResult(RESULT_OK, replyIntent);
               }
               finish();
           }
       });
   }
}

12.2 Ajouter du code pour insérer un mot dans la base de données

  1. Définir un code de demande en tant que membre de la MainActivity.
public static final int NEW_WORD_ACTIVITY_REQUEST_CODE = 1;
  1. Dans MainActivity, ajoutez le rappel onActivityResult() pour NewWordActivity. Si l'activité retourne avec RESULT_OK, insérez le mot retourné dans la base de données en appelant la méthode insert() de WordViewModel.
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == NEW_WORD_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {
            Word word = new Word(data.getStringExtra(NewWordActivity.EXTRA_REPLY));
            mWordViewModel.insert(word);
        } else {
            Toast.makeText(
                    getApplicationContext(),
                    R.string.empty_not_saved,
                    Toast.LENGTH_LONG).show();
        }
}
  1. Dans MainActivity, démarrez NewWordActivity lorsque l'utilisateur appuie sur le FAB. Dans la méthode onCreate() de MainActivity, recherchez le FAB et remplacez le code de onClick() avec le code suivant :
binding.fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, NewWordActivity.class);
                startActivityForResult(intent, NEW_WORD_ACTIVITY_REQUEST_CODE);
            }
});
  1. Maintenant, lancez votre application ! Lorsque vous ajoutez un mot à la base de données dans NewWordActivity, l'interface utilisateur se met automatiquement à jour.

Maintenant que vous avez une application fonctionnelle, résumons ce que vous avez construit. Voici à nouveau la structure de l'application, depuis le début :

Pour continuer à travailler avec l'application RoomWordsSample et apprendre d'autres façons d'utiliser une base de données Room, consultez le codelab 4.2B : Suppression de données d'une base de données Room, qui reprend là où ce codelab s'arrête.

Documentation pour développeurs Android

Blogs et articles:

Codelabs:

Vidéos:

Échantillons de code: