Enregistrer l'état avec des fragments

Plusieurs opérations système Android peuvent affecter l'état de votre fragment. Pour garantir l'enregistrement de l'état de l'utilisateur, le framework Android sauvegarde, et restaure automatiquement les fragments et la pile "Retour". Par conséquent, vous devez vous assurer que toutes les données de votre fragment sont également enregistrées et restaurées.

Le tableau suivant décrit les opérations qui entraînent la perte de l'état de votre fragment et indique si les différents types d'état persistent lors de ces modifications. Les types d'état mentionnés dans le tableau sont les suivants :

  • Variables : variables locales dans le fragment.
  • État de la vue : toutes les données appartenant à une ou plusieurs vues dans le fragment.
  • SavedState : données inhérentes à cette instance de fragment qui doivent être enregistrées dans onSaveInstanceState().
  • NonConfig : données extraites d'une source externe, comme un serveur ou un dépôt local, ou données créées par l'utilisateur qui sont envoyées à un serveur une fois la validation effectuée.

Souvent, les variables sont traitées de la même manière que SavedState, mais le tableau suivant fait la distinction entre les deux pour illustrer l'effet des différentes opérations sur chacune d'elles.

Conditions de fonctionnement Variables État de la vue SavedState NonConfig
Ajouté à la pile "Retour" x
Changement de configuration x
Fin/Recréation du processus x ✓*
Supprimé. L'élément n'a pas été ajouté à la pile "Retour" x x x x
Hôte terminé x x x x

* L'état "NonConfig" peut être conservé en cas de fin du processus à l'aide du module "Saved State" enregistré pour ViewModel.

Tableau 1 : Plusieurs opérations destructives fragmentées et leurs effets sur les différents types d'états.

Prenons un exemple spécifique. Prenons l'exemple d'un écran qui génère une chaîne aléatoire, l'affiche dans un TextView et offre une option pour la modifier avant de l'envoyer à un ami :

Application de génération de texte aléatoire qui illustre différents types d'états.
Figure 1. Application de génération de texte aléatoire qui illustre divers types d'états.

Pour cet exemple, supposons qu'une fois que l'utilisateur appuie sur le bouton de modification, l'application affiche une vue EditText dans laquelle l'utilisateur peut modifier le message. Si l'utilisateur clique sur CANCEL (ANNULER), la vue EditText doit être effacée, et sa visibilité est définie sur View.GONE. Un tel écran peut nécessiter la gestion de quatre données pour une expérience fluide :

Données Type Type d'État Description
seed Long NonConfig Sources utilisées pour générer un nouvelle bonne action de manière aléatoire. Généré lors de la création de ViewModel.
randomGoodDeed String SavedState + Variable Généré lors de la toute première création du fragment. randomGoodDeed est enregistré pour garantir que les utilisateurs voient la même bonne action au hasard, même après la fin et la recréation du processus.
isEditing Boolean SavedState + Variable Indicateur booléen défini sur true lorsque l'utilisateur commence à apporter des modifications. isEditing est enregistré pour que la partie de modification de l'écran reste visible lors de la recréation du fragment.
Texte modifié Editable État de la vue (appartenant à EditText) Texte modifié dans la vue EditText. La vue EditText enregistre ce texte pour éviter que les modifications en cours de l'utilisateur ne soient perdues.

Tableau 2 : États que l'application de génération aléatoire de texte doit gérer.

Les sections suivantes expliquent comment gérer correctement l'état de vos données via des opérations destructives.

État de la vue

Les vues sont responsables de la gestion de leur propre état. Par exemple, lorsqu'une vue accepte les entrées utilisateur, il lui incombe d'enregistrer et de restaurer cette entrée pour gérer les modifications de configuration. Toutes les vues fournies par le framework Android ont leur propre mise en œuvre de onSaveInstanceState() et de onRestoreInstanceState(). Vous n'avez donc pas besoin de gérer l'état de la vue dans votre fragment.

Par exemple, dans le scénario précédent, la chaîne modifiée est stockée dans un objet EditText. Un objet EditText connaît la valeur du texte qu'il affiche, ainsi que d'autres détails, tels que le début et la fin du texte sélectionné.

Une vue a besoin d'un ID pour conserver son état. Cet ID doit être unique dans le fragment et sa hiérarchie de vues. Les vues sans ID ne peuvent pas conserver leur état.

<EditText
    android:id="@+id/good_deed_edit_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

Comme indiqué dans le tableau 1, les vues enregistrent et restaurent ViewState via toutes les opérations qui ne suppriment pas le fragment ni ne détruisent l'hôte.

SavedState

Votre fragment est responsable de la gestion de petites quantités d'état dynamique, qui sont essentielles au fonctionnement du fragment. Vous pouvez conserver facilement les données sérialisées à l'aide de Fragment.onSaveInstanceState(Bundle). Comme pour Activity.onSaveInstanceState(Bundle), les données que vous placez dans le bundle sont conservées via les modifications de configuration ainsi que le traitement de la fin et de la recréation de processus. Elles sont disponibles dans les méthodes onCreate(Bundle), onCreateView(LayoutInflater, ViewGroup, Bundle) et onViewCreated(View, Bundle) de votre fragment.

Poursuivons notre exemple précédent. randomGoodDeed est l'acte présenté à l'utilisateur et isEditing permet de déterminer si le fragment affiche ou masque EditText. Cet état enregistré doit être conservé avec onSaveInstanceState(Bundle), comme illustré ci-après :

Kotlin

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putBoolean(IS_EDITING_KEY, isEditing)
    outState.putString(RANDOM_GOOD_DEED_KEY, randomGoodDeed)
}

Java

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean(IS_EDITING_KEY, isEditing);
    outState.putString(RANDOM_GOOD_DEED_KEY, randomGoodDeed);
}

Pour restaurer l'état dans onCreate(Bundle), récupérez la valeur stockée à partir du bundle :

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    isEditing = savedInstanceState?.getBoolean(IS_EDITING_KEY, false)
    randomGoodDeed = savedInstanceState?.getString(RANDOM_GOOD_DEED_KEY)
            ?: viewModel.generateRandomGoodDeed()
}

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {
        isEditing = savedInstanceState.getBoolean(IS_EDITING_KEY, false);
        randomGoodDeed = savedInstanceState.getString(RANDOM_GOOD_DEED_KEY);
    } else {
        randomGoodDeed = viewModel.generateRandomGoodDeed();
    }
}

Comme indiqué dans le tableau 1, notez que les variables sont conservées lorsque le fragment est placé sur la pile "Retour". Les traiter comme un état enregistré garantit qu'ils persistent dans toutes les opérations destructives.

NonConfig

Les données NonConfig doivent être placées en dehors de votre fragment, comme dans un objet ViewModel. Dans l'exemple précédent, seed (notre état NonConfig) est généré dans ViewModel. La logique de maintien de son état appartient à ViewModel.

Kotlin

public class RandomGoodDeedViewModel : ViewModel() {
    private val seed = ... // Generate the seed

    private fun generateRandomGoodDeed(): String {
        val goodDeed = ... // Generate a random good deed using the seed
        return goodDeed
    }
}

Java

public class RandomGoodDeedViewModel extends ViewModel {
    private Long seed = ... // Generate the seed

    private String generateRandomGoodDeed() {
        String goodDeed = ... // Generate a random good deed using the seed
        return goodDeed;
    }
}

La classe ViewModel permet intrinsèquement de survivre aux modifications de configuration, telles que les rotations d'écran. Elle reste en mémoire lorsque le fragment est placé sur la pile "Retour". Après la fin et la recréation du processus, ViewModel est recréé et un nouvel élément seed est généré. L'ajout d'un module SavedState à votre ViewModel permet au ViewModel de conserver un état simple via la fin et la recréation du processus.

Ressources supplémentaires

Pour en savoir plus sur la gestion de l'état des fragments, consultez les ressources supplémentaires suivantes.

Ateliers de programmation

Guides