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.
Ce codelab pratique fait suite au 4.2A : Room, LiveData et ViewModel
. Ce codelab vous permet de vous entraîner davantage à utiliser l'API fournie par la bibliothèque Room pour implémenter des fonctionnalités de base de données. Vous ajouterez la possibilité de supprimer des éléments spécifiques de la base de données. Ce codelab comprend également un défi de codage, dans lequel vous mettez à jour l'application pour que l'utilisateur puisse modifier les données existantes.
Vous devriez être capable de créer et d'exécuter des applications dans Android Studio Ladybug (2024.2.1) ou une version supérieure. En particulier, vous devez être familiarisé avec les éléments suivants :
RecyclerView
et des adaptateursRoomDatabase
pour stocker et récupérer des données dans la base de données SQLite intégrée d'Android. Vous avez appris ces sujets dans 4.2A : Room, LiveData et ViewModel
.RoomWordsSample
pour conserver les données lorsque l'application se ferme.Vous allez étendre l'application RoomWordsSample que vous avez créée dans le codelab précédent. Jusqu'à présent, cette application affiche une liste de mots et les utilisateurs peuvent ajouter des mots. Lorsque l'application est fermée et rouverte, elle réinitialise la base de données. Les mots que l'utilisateur a ajoutés sont perdus.
Dans ce TP, vous étendez l'application afin qu'elle initialise les données de la base de données uniquement s'il n'y a pas de données existantes.
Ensuite, vous ajoutez un élément de menu qui permet à l'utilisateur de supprimer toutes les données.
Vous permettez également à l'utilisateur de balayer un mot pour le supprimer de la base de données.
L'application RoomWordsSample
que vous avez créée dans le précédent exercice pratique supprime et recrée les données chaque fois que l'utilisateur ouvre l'application. Ce comportement n'est pas idéal, car les utilisateurs souhaitent que leurs mots ajoutés restent dans la base de données lorsque l'application est fermée. (Le code de la solution de l'exercice précédent se trouve sur GitHub.)
Dans cette tâche, vous devez mettre à jour l'application afin que, lorsqu'elle s'ouvre, le jeu de données initial ne soit ajouté que si la base de données ne contient pas de données.
Pour détecter si la base de données contient déjà des données, vous pouvez exécuter une requête pour obtenir un élément de données. Si la requête ne renvoie rien, alors la base de données est vide.
Actuellement, l'interface WordDao
possède une méthode pour obtenir tous les mots, mais pas pour obtenir un mot spécifique. La méthode pour obtenir un mot spécifique n'a pas besoin de renvoyer LiveData
, car votre application appellera la méthode explicitement lorsque cela est nécessaire.
Dans l'interface WordDao
, ajoutez une méthode pour obtenir n'importe quel mot.
@Query("SELECT * from word_table LIMIT 1")
Word[] getAnyWord();
Room
déclenche la requête de base de données lorsque la méthode getAnyWord()
est appelée et renvoie un tableau contenant un mot. Vous n'avez pas besoin d'écrire de code supplémentaire pour l'implémenter.
Utilisez la méthode getAnyWord()
dans la méthode qui initialise la base de données. S'il y a des données, laissez-les telles quelles. S'il n'y a pas de données, ajoutez l'ensemble de données initial.
WordRoomDatbase
, mettez à jour le sRoomDatabaseCallback
pour vérifier si la base de données contient des mots avant d'initialiser les données.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();
// Si nous n'avons pas de mots, alors créons la liste initiale de mots.
if (dao.getAnyWord().length < 1) {
Word word = new Word("Hello");
dao.insert(word);
word = new Word("World");
dao.insert(word);
}
});
Dans le précédent exercice pratique, vous avez utilisé la méthode deleteAll()
pour effacer toutes les données à l'ouverture de la base de données. La méthode deleteAll()
n'était appelée que depuis la fonction d'appel sRoomDatabaseCallback
au démarrage de l'application. Vous allez maintenant rendre la méthode deleteAll()
disponible via le ViewModel
afin que votre application puisse appeler la méthode à tout moment.
Voici les étapes générales pour implémenter une méthode d'utilisation de la bibliothèque Room pour interagir avec la base de données :
deleteAll()
, vous avez déjà effectué cette étape dans le précédent TP.WordRepository
. Écrivez le code pour exécuter la méthode en arrière-plan.WordRepository
, ajoutez la méthode au WordViewModel
. Le reste de l'application pourra alors accéder à la méthode par l'intermédiaire du WordViewModel
.deleteAll()
à l'interface WordDao
et annotez-la.Dans WordDao
, vérifiez que la méthode deleteAll()
est définie et annotée avec le SQL qui s'exécute lorsque la méthode s'exécute.
@Query("DELETE FROM word_table")
void deleteAll();
deleteAll
()
à la classe WordRepository
Add the deleteAll()
method to the WordRepository
and use ExecutorService to delete all words in the background.
public void deleteAll(){
WordRoomDatabase.databaseWriteExecutor.execute(() -> mWordDao.deleteAll());
}
deleteAll
()
à la classe WordViewModel
Rendez la méthode deleteAll()
disponible pour l'activité principale en l'ajoutant au WordViewModel
.
Dans la classe WordViewModel, ajoutez la méthode deleteAll():
public void deleteAll() {mRepository.deleteAll();}
Ensuite, vous ajoutez une option au menu pour permettre aux utilisateurs de supprimer toutes les données.
menu_main.xml
, modifiez le titre "Clear all data
" et l'identifiant de l'option de menu, comme suit :<item
android:id="@+id/clear_data"
android:orderInCategory="100"
android:title="@string/clear_all_data"
app:showAsAction="never" />
MainActivity
, implémentez la méthode onOptionsItemSelected()
pour appeler la méthode deleteAll()
sur l'objet WordViewModel
.public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.clear_data) {
// Ajouter un toast de confirmation
Toast.makeText(this, "Clearing the data...",
Toast.LENGTH_SHORT).show();
// Effacer les données existantes
mWordViewModel.deleteAll();
return true;
}
return super.onOptionsItemSelected(item);
}
Clear all data"
. Tous les mots devraient disparaître.Votre application permet aux utilisateurs d'ajouter des mots et de supprimer tous les mots. Dans les tâches 4 et 5, vous étendrez l'application afin que les utilisateurs puissent supprimer un mot en faisant glisser l'élément dans le RecyclerView
.
Encore une fois, voici les étapes générales pour implémenter une méthode d'utilisation de la bibliothèque Room pour interagir avec la base de données :
WordRepository
. Écrivez le code pour exécuter la méthode en arrière-plan.WordRepository
, ajoutez la méthode à WordViewModel
. Le reste de l'application peut alors accéder à la méthode via WordViewModel
.deleteWord
()
à la DAO et l'annoter.Dans WordDao
, ajoutez la méthode deleteWord()
.
@Delete
void deleteWord(Word word);
Comme cette opération supprime une seule ligne, l'annotation @Delete
est tout ce qui est nécessaire pour supprimer le mot de la base de données.
deleteWord
()
à la classe WordRepository
WordRepository
, ajoutez la méthode deleteWord()
pour supprimer un mot en appelant deleteWord()
à partir du DAO en utilisant ExecutorService
.public void deleteWord(Word word) {
WordRoomDatabase.databaseWriteExecutor.execute(() -> mWordDao.deleteWord(word));
}
deleteWord
()
à la classe WordViewModel
Pour rendre la méthode deleteWord()
disponible aux autres classes de l'application, en particulier MainActivity
, ajoutez-la à WordViewModel
.
public void deleteWord(Word word) {mRepository.deleteWord(word);}
Vous avez maintenant implémenté la logique pour supprimer un mot. Cependant, il n'existe pas encore de moyen d'appeler l'opération de suppression de mots depuis l'interface utilisateur de l'application. Vous allez résoudre ce problème ensuite.
Dans cette tâche, vous ajoutez une fonctionnalité pour permettre aux utilisateurs de balayer un élément de la RecyclerView
pour le supprimer.
Utilisez la classe ItemTouchHelper
fournie par la bibliothèque de support Android (Androidx) pour implémenter la fonctionnalité de glissement dans votre RecyclerView
. La classe ItemTouchHelper
possède les méthodes suivantes :
onMove()
est appelée lorsque l'utilisateur déplace l'élément. Vous n'implémenterez aucune fonctionnalité de déplacement dans cette application.onSwipe()
est appelée lorsque l'utilisateur glisse l'élément. Vous implémentez cette méthode pour supprimer le mot qui a été glissé.WordListAdapter
, ajouter une méthode pour obtenir le mot à une position donnée.public Word getWordAtPosition (int position) {
return mWords.get(position);
}
MainActivity
, dans onCreate()
, créez le ItemTouchHelper
. Attachez le ItemTouchHelper
au RecyclerView
.// Ajouter la fonctionnalité de glisser pour supprimer les éléments de la recyclerview.
ItemTouchHelper helper = new ItemTouchHelper(
new ItemTouchHelper.SimpleCallback(0,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder,
@NonNull RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder,
int direction) {
int position = viewHolder.getAdapterPosition();
Word swipedWord = mAdapter.getWordAtPosition(position);
Toast.makeText(MainActivity.this, "Deleting " +
swipedWord.getWord(), Toast.LENGTH_LONG).show();
// Supprimer le mot
mWordViewModel.deleteWord(swipedWord);
}
});
helper.attachToRecyclerView(binding.contentMain.recyclerview);
Points à noter dans le code:
viewHolder.getAdapterPosition()
: onSwiped()
récupère la position du ViewHolder
qui a été glissé.mAdapter.getWordAtPosition(position)
: En fonction de la position, vous pouvez obtenir le mot affiché par le ViewHolder
en appelant la méthode getWordAtPosition()
que vous avez définie dans l'adaptateur.mWordViewModel.deleteWord(swipedWord)
: Supprime le mot en appelant deleteWord()
sur le WordViewModel
.Défi : Mettez à jour votre application pour permettre aux utilisateurs de modifier un mot en appuyant sur le mot, puis en sauvegardant leurs modifications.
NewWordActivity
Vous pouvez ajouter des fonctionnalités à NewWordActivity
, afin qu'elle puisse être utilisée soit pour ajouter un nouveau mot, soit pour modifier un mot existant.
Word
La classe d'entité Word
utilise le champ word comme clé de base de données. Cependant, lorsque vous mettez à jour une ligne dans la base de données, l'élément mis à jour ne peut pas être la clé primaire, car la clé primaire est unique à chaque ligne et ne change jamais. Vous devez donc ajouter un identifiant id
généré automatiquement à utiliser comme clé primaire.
@PrimaryKey(autoGenerate = true)
private int id;
@NonNull
@ColumnInfo(name = "word")
private String mWord;
Word
qui prend un id
Ajoutez un constructeur à la classe d'entité Word
qui prend id et word comme paramètres. Assurez-vous que ce constructeur supplémentaire est annoté à l'aide de @Ignore
, car Room attend par défaut un seul constructeur dans une classe d'entité.
@Ignore
public Word(int id, @NonNull String word) {
this.id = id;
this.mWord = word;
}
Pour mettre à jour un mot existant, créez le mot en utilisant ce constructeur. Room utilisera la clé primaire (dans ce cas, l'id
) pour trouver l'entrée existante dans la base de données afin qu'elle puisse être mise à jour.
Dans WordDao
, ajoutez la méthode update()
comme ceci :
@Update
void update(Word... word);
Dans WordRoomDatabase
, augmentez le numéro de version de la base de données, car la structure de la table de la base de données a été modifiée.
@Database(entities = {Word.class}, version = 2, exportSchema = false)
Room
se charge d'ouvrir et de fermer les connexions à la base de données chaque fois qu'une opération de base de données est exécutée.@insert
, @delete
, @update
, @query
.Par exemple:
@Delete
void deleteWord(Word word);
@Update
void update(Word... word);
@query
et fournissez le SQL de l'opération.Par exemple:
@Query("SELECT * from word_table ORDER BY word ASC")
LiveData<List<Word>> getAllWords();
@Query("DELETE FROM word_table")
void deleteAll();
ItemTouchHelper
RecyclerView
, vous pouvez utiliser la classe ItemTouchHelper
.onMove()
et onSwipe()
.RecyclerView
. La méthode prend une position en entrée et renvoie l'élément correspondant. Appelez la méthode dans onMove()
ou onSwipe()
.Entities, data access objects (DAOs), et ViewModel
:
ViewModel
guideDao
référenceViewModel
référenceItemTouchHelper
:
ItemTouchHelper
reference