Livre de recettes sur grand écran

Android vous fournit tous les ingrédients dont vous avez besoin pour "concocter" des applications grand écran 5 étoiles. Les recettes de ce livre sélectionnent et combinent des ingrédients de choix pour résoudre des problèmes de développement spécifiques. Chaque recette inclut des bonnes pratiques, des exemples de code de qualité et des instructions détaillées pour vous aider à devenir un "top chef" des applications grand écran.

Notes de 1 à 5 étoiles

Les recettes sont évaluées en fonction de leur niveau d'adéquation avec les consignes relatives à la qualité des applis sur les grands écrans.

Note 5 étoiles Répond aux critères du niveau 1 (différencié pour les grands écrans)
Note 4 étoiles Répond aux critères du niveau 2 (optimisé pour les grands écrans)
Note 3 étoiles Répond aux critères de niveau 3 (adapté aux grands écrans)
Note 2 étoiles Propose des fonctionnalités sur grand écran, mais ne respecte pas les consignes relatives à la qualité des applications sur grand écran
Note 1 étoile Répond aux besoins d'un cas d'utilisation spécifique, mais n'est pas adapté aux grands écrans

Prise en charge de l'appareil photo Chromebook

Note 3 étoiles

Faites-vous remarquer sur Google Play par les utilisateurs de Chromebook.

Si votre application peut être utilisée avec des fonctionnalités de caméra/appareil photo de base seulement, ne laissez pas les plates-formes de téléchargement d'applications empêcher les utilisateurs de Chromebooks d'installer l'application juste parce que vous avez accidentellement spécifié des fonctionnalités avancées de caméra/appareil photo que l'on trouve sur les téléphones haut de gamme.

Les Chromebooks sont dotés d'une caméra frontale intégrée (orientée vers l'utilisateur) qui convient aux visioconférences, aux instantanés et à d'autres usages. Toutefois, tous les Chromebooks ne sont pas équipés d'une caméra arrière, orientée vers l'extérieur. De plus, la plupart des caméras frontales sur les Chromebooks ne sont pas compatibles avec la mise au point automatique ou le flash.

Bonnes pratiques

Les applications de caméra/appareil photo polyvalentes sont compatibles avec tous les appareils, quelle que soit leur configuration : appareils avec caméra frontale, caméra arrière, caméra externe connectée par USB.

Pour vous assurer que les plates-formes de téléchargement d'applications proposent votre application sur le plus grand nombre d'appareils possible, déclarez toujours toutes les fonctionnalités de caméra/appareil photo utilisées par votre application et indiquez explicitement si elles sont requises.

Ingrédients

  • Autorisation CAMERA : autorise votre application à accéder aux caméras d'un appareil.
  • Élément <uses-feature> du fichier manifeste : informe les plates-formes de téléchargement d'applications des fonctionnalités utilisées par votre application.
  • Attribut required : indique aux plates-formes de téléchargement d'applications si votre application peut fonctionner sans fonctionnalité spécifique.

Étapes

Résumé

Déclarez l'autorisation CAMERA. Déclarez les fonctionnalités de caméra/appareil photo qui fournissent une compatibilité de base. Indiquez si chaque fonctionnalité est obligatoire ou non.

1. Déclarer l'autorisation CAMERA

Ajoutez l'autorisation suivante au fichier manifeste de l'application :

<uses-permission android:name="android.permission.CAMERA" />
2. Déclarer les fonctionnalités de base de la caméra ou de l'appareil photo

Ajoutez les fonctionnalités suivantes au fichier manifeste de l'application :

<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
3. Indiquer si chaque fonctionnalité est obligatoire

Définissez android:required="false" pour la fonctionnalité android.hardware.camera.any afin d'autoriser l'accès à votre application sur les appareils qui intègrent une caméra externe ou intégrée, voire aucune caméra.

Pour les autres fonctionnalités, configurez android:required="false" afin que les appareils tels que les Chromebooks sans caméra arrière, sans mise au point automatique ou sans Flash puissent accéder à votre application sur les plates-formes de téléchargement.

Résultats

Les utilisateurs de Chromebooks peuvent télécharger et installer votre application depuis Google Play et d'autres plates-formes de téléchargement d'applications. De plus, les appareils qui offrent une prise en charge complète, comme les téléphones, pourront exploiter toutes les fonctionnalités de caméra/appareil photo.

Maintenant que vous avez défini explicitement les fonctionnalités de caméra/appareil photo compatibles avec votre application et spécifié celles qu'elle requiert, votre application est accessible à un maximum d'appareils.

Ressources supplémentaires

Pour en savoir plus, consultez la section sur les fonctionnalités matérielles liées à l'appareil photo dans la documentation <uses-feature>.

Orientation de l'application limitée sur les téléphones, mais pas sur les appareils à grand écran

Note 2 étoiles

Votre application fonctionne parfaitement sur les téléphones en mode portrait. Vous avez donc limité l'application à ce mode. Toutefois, vous savez que le mode paysage permettrait d'en faire plus sur les grands écrans.

Comment limiter l'orientation portrait de l'application aux petits écrans, tout en permettant le mode paysage sur les grands écrans ?

Bonnes pratiques

Les meilleures applications respectent les préférences utilisateur telles que l'orientation de l'appareil.

Les consignes relatives à la qualité des applis sur les grands écrans recommandent que les applications soient compatibles avec toutes les configurations d'appareil, y compris les orientations portrait et paysage, le mode multifenêtre ou l'état plié et déplié des appareils pliables. Les applications doivent optimiser les mises en page et les interfaces utilisateur en fonction des différentes configurations. Elles doivent aussi enregistrer et restaurer l'état lors des changements de configuration.

La recette qui suit est une mesure temporaire : elle offre un zeste de compatibilité avec les grands écrans. Utilisez cette recette jusqu'à ce que votre application soit entièrement compatible avec toutes les configurations d'appareils.

Ingrédients

  • screenOrientation : paramètre de fichier manifeste d'application qui vous permet de spécifier la manière dont votre application réagit aux changements d'orientation de l'appareil.
  • Jetpack WindowManager: ensemble de bibliothèques vous permettant de déterminer la taille et le format de la fenêtre de l'application. Rétrocompatible avec le niveau d'API 14.
  • Activity#setRequestedOrientation() : méthode vous permettant de modifier l'orientation de l'application au moment de l'exécution.

Étapes

Résumé

Autorisez l'application à gérer les changements d'orientation par défaut dans son fichier manifeste. Au moment de l'exécution, déterminez la taille de la fenêtre de l'application. Si elle est petite, limitez l'orientation de l'application en remplaçant le paramètre d'orientation du fichier manifeste.

1. Spécifier le paramètre d'orientation dans le fichier manifeste de l'application

Vous pouvez éviter de déclarer l'élément screenOrientation du fichier manifeste d'application (dans ce cas, l'orientation par défaut est unspecified) ou définir l'orientation de l'écran sur fullUser. Si l'utilisateur n'a pas verrouillé la rotation basée sur le capteur, votre application accepte toutes les orientations d'appareil.

<activity
    android:name=".MyActivity"
    android:screenOrientation="fullUser">

La différence entre unspecified et fullUser est subtile, mais importante. Si vous ne déclarez pas de valeur screenOrientation, le système choisit l'orientation. La règle utilisée pour définir l'orientation peut varier d'un appareil à l'autre. En revanche, spécifier fullUser correspond mieux au comportement que l'utilisateur a défini pour l'appareil : si l'utilisateur a verrouillé la rotation basée sur le capteur, l'application suit ses préférences. Dans le cas contraire, le système autorise l'une des quatre orientations d'écran possibles (portrait, paysage, portrait inversé ou paysage inversé). Voir android:screenOrientation.

2. Déterminer la taille de l'écran

Comme le fichier manifeste permet toutes les orientations autorisées par l'utilisateur, vous pouvez spécifier l'orientation de l'application par programmation en fonction de la taille de l'écran.

Ajoutez les bibliothèques Jetpack WindowManager au fichier build.gradle ou build.gradle.kts du module:

Kotlin

implementation("androidx.window:window:version")
implementation("androidx.window:window-core:version")

Groovy

implementation 'androidx.window:window:version'
implementation 'androidx.window:window-core:version'

Utilisez la méthode Jetpack WindowManager WindowMetricsCalculator#computeMaximumWindowMetrics() pour obtenir la taille d'écran de l'appareil en tant qu'objet WindowMetrics. Les métriques de fenêtre peuvent être comparées aux classes de taille de fenêtre pour décider quand limiter l'orientation.

Les classes de taille Windows fournissent des points d'arrêt entre les petits et les grands écrans.

Utilisez les points d'arrêt WindowWidthSizeClass#COMPACT et WindowHeightSizeClass#COMPACT pour déterminer la taille de l'écran :

Kotlin

/** Determines whether the device has a compact screen. **/
fun compactScreen() : Boolean {
    val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this)
    val width = metrics.bounds.width()
    val height = metrics.bounds.height()
    val density = resources.displayMetrics.density
    val windowSizeClass = WindowSizeClass.compute(width/density, height/density)

    return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT ||
        windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT
}

Java

/** Determines whether the device has a compact screen. **/
private boolean compactScreen() {
    WindowMetrics metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this);
    int width = metrics.getBounds().width();
    int height = metrics.getBounds().height();
    float density = getResources().getDisplayMetrics().density;
    WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density);
    return windowSizeClass.getWindowWidthSizeClass() == WindowWidthSizeClass.COMPACT ||
                windowSizeClass.getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT;
}
    Remarque :
  • Les exemples ci-dessus sont implémentés en tant que méthodes d'une activité. Dès lors, l'activité est "déréférencée" avec l'attribut this dans l'argument de computeMaximumWindowMetrics().
  • La méthode computeMaximumWindowMetrics() est utilisée à la place de computeCurrentWindowMetrics(), car l'application peut être lancée en mode multifenêtre, ce qui ignore le paramètre d'orientation de l'écran. Il est inutile de déterminer la taille de la fenêtre de l'application et de remplacer le paramètre d'orientation, sauf si la fenêtre de l'application correspond à la totalité de l'écran de l'appareil.

Consultez la section WindowManager pour découvrir comment déclarer des dépendances afin de rendre la méthode computeMaximumWindowMetrics() disponible dans votre application.

3. Remplacer le paramètre du fichier manifeste d'application

Une fois que vous avez déterminé que l'appareil est doté d'une taille d'écran compacte, vous pouvez appeler Activity#setRequestedOrientation() pour remplacer le paramètre screenOrientation du fichier manifeste :

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    requestedOrientation = if (compactScreen())
        ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
        ActivityInfo.SCREEN_ORIENTATION_FULL_USER
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    val container: ViewGroup = binding.container

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(object : View(this) {
        override fun onConfigurationChanged(newConfig: Configuration?) {
            super.onConfigurationChanged(newConfig)
            requestedOrientation = if (compactScreen())
                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
                ActivityInfo.SCREEN_ORIENTATION_FULL_USER
        }
    })
}

Java

@Override
protected void onCreate(Bundle savedInstance) {
    super.onCreate(savedInstanceState);
    if (compactScreen()) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
    }
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    ViewGroup container = binding.container;

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(new View(this) {
        @Override
        protected void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            if (compactScreen()) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
            }
        }
    });
}

En ajoutant la logique aux méthodes onCreate() et View.onConfigurationChanged(), vous pouvez obtenir les métriques de fenêtre maximales et remplacer le paramètre d'orientation chaque fois que l'activité est redimensionnée ou déplacée entre les écrans, par exemple après une rotation de l'appareil ou lorsqu'un appareil pliable est plié ou déplié. Pour savoir quand des modifications de configuration se produisent et à quel moment elles provoquent la recréation d'une activité, consultez la section Gérer les modifications de configuration.

Résultats

Votre application devrait désormais rester en mode portrait sur les petits écrans, quelle que soit la rotation de l'appareil. Sur les grands écrans, elle devrait être compatible avec les orientations paysage et portrait.

Ressources supplémentaires

Pour découvrir comment mettre à niveau de votre application afin qu'elle soit toujours compatible avec toutes les configurations d'appareil, consultez les ressources suivantes :

Pause et reprise de la lecture de contenus multimédias avec la barre d'espace du clavier externe

Note 4 étoiles

L'optimisation pour les grands écrans doit permettre les interactions au moyen d'un clavier externe, par exemple lorsque l'utilisateur appuie sur la barre d'espace pour mettre en pause ou reprendre la lecture de vidéos et d'autres contenus multimédias. Cela est particulièrement utile pour les tablettes, qui sont souvent connectées à des claviers externes, et pour les Chromebooks, qui sont généralement équipés de claviers externes, mais qui peuvent être utilisés en mode Tablette.

Lorsque le contenu multimédia est le seul élément de la fenêtre (par exemple, durant la lecture d'une vidéo en plein écran), répondez aux événements de pression sur une touche au niveau de l'activité ou, dans Jetpack Compose, au niveau de l'écran.

Bonnes pratiques

Chaque fois que votre application lit un fichier multimédia, les utilisateurs doivent pouvoir suspendre et reprendre la lecture en appuyant sur la barre d'espace d'un clavier physique.

Ingrédients

  • KEYCODE_SPACE : constante de code de touche pour la barre d'espace.

Compose

  • onPreviewKeyEvent : Modifier qui permet à un composant d'intercepter les événements de touche matérielle lorsque celui-ci (ou l'un de ses enfants) est sélectionné.
  • onKeyEvent : à l'instar de onPreviewKeyEvent, ce Modifier permet à un composant d'intercepter les événements de touche matérielle lorsque celui-ci (ou l'un de ses enfants) est sélectionné.

Vues

  • onKeyUp() : appelé lorsqu'une touche est relâchée et qu'elle n'est pas gérée par une vue dans une activité.

Étapes

Résumé

Les applications basées sur les vues et basées sur Jetpack Compose réagissent aux pressions sur les touches du clavier de la même manière : l'application doit écouter les événements de pression sur les touches, les filtrer et répondre aux pressions de touches sélectionnées, comme une barre d'espace.

1. Écouter les événements de clavier

Vues

Dans une activité dans votre application, remplacez la méthode onKeyUp() :

Kotlin

override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
    ...
}

Java

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    ...
}

Cette méthode est appelée chaque fois qu'une touche est relâchée. Elle se déclenche exactement une fois pour chaque frappe.

Compose

Avec Jetpack Compose, vous pouvez tirer parti du modificateur onPreviewKeyEvent ou onKeyEvent au sein de l'écran qui gère la frappe :

Column(modifier = Modifier.onPreviewKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp) {
        ...
    }
    ...
})

ou

Column(modifier = Modifier.onKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp) {
        ...
    }
    ...
})

2. Filtrer les appuis sur la barre d'espace

Dans la méthode onKeyUp(), ou dans les méthodes des modificateurs onPreviewKeyEvent et onKeyEvent Compose, filtrez KeyEvent.KEYCODE_SPACE pour envoyer l'événement approprié à votre composant multimédia :

Vues

Kotlin

if (keyCode == KeyEvent.KEYCODE_SPACE) {
    togglePlayback()
    return true
}
return false

Java

if (keyCode == KeyEvent.KEYCODE_SPACE) {
    togglePlayback();
    return true;
}
return false;

Compose

Column(modifier = Modifier.onPreviewKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
        ...
    }
    ...
})

ou

Column(modifier = Modifier.onKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
        ...
    }
    ...
})

Résultats

Votre application peut désormais réagir aux appuis sur la barre d'espace pour mettre en pause et reprendre la lecture d'une vidéo ou d'un autre type de contenu multimédia.

Ressources supplémentaires

Pour en savoir plus sur les événements de clavier et leur gestion, consultez la section Gérer la saisie au clavier.

Refus de la paume de la main avec le stylet

Note 5 étoiles

Un stylet peut être un outil incroyablement productif et créatif sur les grands écrans. Toutefois, lorsque les utilisateurs dessinent, écrivent ou interagissent avec une application à l'aide d'un stylet, ils touchent parfois l'écran avec la paume de leur main. L'événement tactile peut être signalé à votre application avant que le système ne le reconnaisse et l'ignore comme une pression involontaire de la paume de la main.

Bonnes pratiques

Votre application doit identifier les événements tactiles involontaires et les ignorer. Android annule une pression de la paume de la main en envoyant un objet MotionEvent. Vérifiez si l'objet contient ACTION_CANCEL ou ACTION_POINTER_UP et FLAG_CANCELED afin de déterminer s'il faut rejeter le geste causé par la pression de la paume de la main.

Ingrédients

  • MotionEvent : représente les événements tactiles et de mouvement. Contient les informations nécessaires pour déterminer si un événement doit être ignoré.
  • OnTouchListener#onTouch() : reçoit des objets MotionEvent.
  • MotionEvent#getActionMasked() : renvoie l'action associée à un événement de mouvement.
  • ACTION_CANCEL : constante MotionEvent indiquant qu'un geste doit être annulé.
  • ACTION_POINTER_UP : constante MotionEvent indiquant qu'un autre pointeur que le premier a augmenté (c'est-à-dire qu'il a abandonné le contact avec l'écran de l'appareil).
  • FLAG_CANCELED : constante MotionEvent indiquant que le pointeur a provoqué un événement tactile non intentionnel. Ajout aux événements ACTION_POINTER_UP et ACTION_CANCEL sur Android 13 (niveau d'API 33) ou version ultérieure.

Étapes

Résumé

Examinez les objets MotionEvent envoyés à votre application. Utilisez les API MotionEvent pour déterminer les caractéristiques des événements :

  • Événements à pointeur unique : recherchez ACTION_CANCEL. Sur Android 13 et versions ultérieures, recherchez également FLAG_CANCELED.
  • Événements à pointeurs multiples : sur Android 13 et versions ultérieures, recherchez ACTION_POINTER_UP et FLAG_CANCELED.

Répondez aux événements ACTION_CANCEL et ACTION_POINTER_UP/FLAG_CANCELED.

1. Acquérir des objets d'événement de mouvement

Ajoutez un OnTouchListener à votre application :

Kotlin

val myView = findViewById<View>(R.id.myView).apply {
    setOnTouchListener { view, event ->
        // Process motion event.
    }
}

Java

View myView = findViewById(R.id.myView);
myView.setOnTouchListener( (view, event) -> {
    // Process motion event.
});
2. Déterminer l'action et les indicateurs d'événement

Recherchez ACTION_CANCEL, qui indique un événement à pointeur unique à tous les niveaux d'API. Sur Android 13 ou version ultérieure, vérifiez ACTION_POINTER_UP pour FLAG_CANCELED..

Kotlin

val myView = findViewById<View>(R.id.myView).apply {
    setOnTouchListener { view, event ->
        when (event.actionMasked) {
            MotionEvent.ACTION_CANCEL -> {
                //Process canceled single-pointer motion event for all SDK versions.
            }
            MotionEvent.ACTION_POINTER_UP -> {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
                   (event.flags and MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) {
                    //Process canceled multi-pointer motion event for Android 13 and higher.
                }
            }
        }
        true
    }
}

Java

View myView = findViewById(R.id.myView);
myView.setOnTouchListener( (view, event) -> {
    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_CANCEL:
            // Process canceled single-pointer motion event for all SDK versions.
        case MotionEvent.ACTION_UP:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
               (event.getFlags() & MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) {
                //Process canceled multi-pointer motion event for Android 13 and higher.
            }
    }
    return true;
});
3. Annuler le geste

Après avoir identifié une pression de la paume de la main, vous pouvez annuler les effets à l'écran du geste.

Votre application doit conserver un historique des actions de l'utilisateur pour que les entrées involontaires (comme les pressions avec la paume de la main) puissent être annulées. Pour voir un exemple, consultez Implémenter une application de dessin de base dans l'atelier de programmation Améliorer la prise en charge des stylets dans une application Android.

Résultats

Votre application peut désormais identifier et refuser les gestes de la paume de la main pour les événements à plusieurs pointeurs sur Android 13 ou version ultérieure, et pour les événements à pointeur unique à tous les niveaux d'API.

Ressources supplémentaires

Pour en savoir plus, consultez les ressources suivantes :

Gestion de l'état WebView

Note 3 étoiles

WebView est un composant couramment utilisé qui propose un système avancé de gestion des états. Un WebView doit conserver son état et sa position de défilement lors des modifications de configuration. Un WebView peut perdre la position de défilement lorsque l'utilisateur fait pivoter l'appareil ou déplie un téléphone pliable, ce qui l'oblige à faire défiler à nouveau la WebView du haut jusqu'à la position de défilement précédente.

Bonnes pratiques

Minimisez le nombre de fois où une WebView est recréée. WebView est capable de gérer efficacement son état, et vous pouvez en profiter pour gérer autant de modifications de configuration que possible. Votre application doit gérer les modifications de configuration, car la recréation d'Activity (méthode du système pour gérer les modifications de configuration) recrée également la WebView, ce qui entraîne la perte de l'état de la WebView.

Ingrédients

  • android:configChanges : attribut de l'élément <activity> du fichier manifeste. Recense les modifications de configuration gérées par l'activité.
  • View#invalidate() : lorsque cette méthode est utilisée, une vue est redessinée. Héritée par WebView.

Étapes

Résumé

Pour enregistrer l'état de la WebView, évitez autant que possible la recréation d'Activity et laissez ensuite la WebView s'invalider afin qu'elle puisse se redimensionner tout en conservant son état.

1. Ajouter des modifications de configuration au fichier AndroidManifest.xml de votre application

Pour éviter la recréation de l'activité, spécifiez les modifications de configuration gérées par votre application (plutôt que par le système) :

<activity
  android:name=".MyActivity"
  android:configChanges="screenLayout|orientation|screenSize
      |keyboard|keyboardHidden|smallestScreenSize" />

2. Invalider la WebView chaque fois que votre application reçoit une modification de configuration

Kotlin

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    webView.invalidate()
}

Java

@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    webview.invalidate();
}

Cette étape ne s'applique qu'au système de vues, car Jetpack Compose n'a pas besoin d'invalider quoi que ce soit pour redimensionner correctement les éléments Composable. Cependant, Compose recrée souvent un élément WebView s'il n'est pas géré correctement. Utilisez le wrapper Accompanist WebView pour enregistrer et restaurer l'état de la WebView dans vos applications Compose.

Résultats

Les composants WebView de votre application conservent désormais leur état et leur position de défilement lors de plusieurs modifications de configuration, du redimensionnement au changement d'orientation, en passant par le pliage et le dépliage.

Ressources supplémentaires

Pour en savoir plus sur les modifications de configuration et sur leur gestion, consultez la page Gérer les modifications de configuration.

Gestion de l'état RecyclerView

Note 3 étoiles

RecyclerView peut afficher de grandes quantités de données à l'aide de ressources graphiques minimales. Lorsque RecyclerView fait défiler la liste des éléments, RecyclerView réutilise les instances View des éléments qui défilent hors de l'écran pour créer d'autres éléments lorsqu'ils défilent à l'écran. Toutefois, les modifications de configuration telles que la rotation de l'appareil peuvent réinitialiser l'état d'une RecyclerView, ce qui oblige les utilisateurs à faire défiler l'écran jusqu'à leur position précédente dans la liste des éléments RecyclerView.

Bonnes pratiques

RecyclerView doit conserver son état, en particulier sa position de défilement, ainsi que l'état de ses éléments de liste lors de toutes les modifications de configuration.

Ingrédients

Étapes

Résumé

Définissez la stratégie de restauration d'état de RecyclerView.Adapter pour enregistrer la position de défilement RecyclerView. Enregistrez l'état des éléments de liste de RecyclerView. Ajoutez l'état des éléments de liste à l'adaptateur RecyclerView, puis restaurez l'état des éléments de liste lorsqu'ils sont associés à un ViewHolder.

1. Activer la stratégie de restauration d'état d'Adapter

Activez la stratégie de restauration d'état de l'adaptateur RecyclerView afin que la position de défilement de RecyclerView soit maintenue en cas de modification de la configuration. Ajoutez la spécification de stratégie au constructeur de l'adaptateur :

Kotlin

class MyAdapter() : RecyclerView.Adapter() {
    init {
        stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY
    }
    ...
}

Java

class MyAdapter extends RecyclerView.Adapter {

    public Adapter() {
        setStateRestorationPolicy(StateRestorationPolicy.PREVENT_WHEN_EMPTY);
    }
    ...
}

2. Enregistrer l'état des éléments de liste avec état

Enregistrez l'état d'éléments de liste RecyclerView complexes, tels que les éléments contenant des éléments EditText. Par exemple, pour enregistrer l'état d'un EditText, ajoutez un rappel semblable à un gestionnaire onClick afin de capturer les modifications de texte. Dans le rappel, définissez les données à enregistrer :

Kotlin

input.addTextChangedListener(
    afterTextChanged = { text ->
        text?.let {
            // Save state here.
        }
    }
)

Java

input.addTextChangedListener(new TextWatcher() {
    
    ...

    @Override
    public void afterTextChanged(Editable s) {
        // Save state here.
    }
});

Déclarez le rappel dans votre Activity ou votre Fragment. Utilisez un ViewModel pour stocker l'état.

3. Ajouter l'état de l'élément de liste à Adapter

Ajoutez l'état des éléments de liste à votre RecyclerView.Adapter. Transmettez l'état de l'élément au constructeur de l'adaptateur lors de la création de l'Activity ou Fragment hôte :

Kotlin

val adapter = MyAdapter(items, viewModel.retrieveState())

Java

MyAdapter adapter = new MyAdapter(items, viewModel.retrieveState());

4. Récupérer l'état de l'élément de liste dans le ViewHolder de l'adaptateur

Dans RecyclerView.Adapter, lorsque vous liez un ViewHolder à un élément, restaurez l'état de l'élément :

Kotlin

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    ...
    val item = items[position]
    val state = states.firstOrNull { it.item == item }

    if (state != null) {
        holder.restore(state)
    }
}

Java

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    ...
    Item item = items[position];
    Arrays.stream(states).filter(state -> state.item == item)
        .findFirst()
        .ifPresent(state -> holder.restore(state));
}

Résultats

Votre RecyclerView peut désormais restaurer sa position de défilement et l'état de chaque élément de la liste RecyclerView.

Ressources supplémentaires

Gestion du clavier détachable

Note 3 étoiles

La compatibilité avec les claviers détachables permet de maximiser la productivité des utilisateurs sur les appareils à grand écran. Android déclenche une modification de configuration chaque fois qu'un clavier est connecté ou dissocié d'un appareil, ce qui peut entraîner une perte de l'état de l'interface utilisateur. Votre application peut enregistrer et restaurer son état, ce qui permet au système de gérer la recréation de l'activité, ou restreindre la recréation de l'activité en cas de modification de la configuration du clavier. Dans tous les cas, toutes les données associées au clavier sont stockées dans un objet Configuration. Les champs keyboard et keyboardHidden de l'objet de configuration contiennent des informations sur le type de clavier et sa disponibilité.

Bonnes pratiques

Les applications optimisées pour les grands écrans sont compatibles avec tous les types de périphériques d'entrée, des claviers logiciels et matériels aux stylets, souris, pavés tactiles et autres périphériques.

La prise en charge des claviers externes implique des modifications de configuration que vous pouvez gérer de deux manières:

  1. Laissez le système recréer l'activité en cours d'exécution. Vous devrez vous charger de gérer l'état de votre application.
  2. Gérez vous-même la modification de la configuration (l'activité ne sera pas recréée) :
    • Déclarer toutes les valeurs de configuration liées au clavier
    • Créer un gestionnaire de modifications de configuration

Les applications de productivité, qui nécessitent souvent un contrôle précis de l'interface utilisateur pour la saisie de texte et d'autres entrées, peuvent bénéficier de l'approche personnalisée pour gérer les modifications de configuration.

Dans certains cas, vous pouvez modifier la mise en page de votre application lorsqu'un clavier physique est connecté ou dissocié, par exemple pour libérer de l'espace pour les outils ou les fenêtres de modification.

Étant donné que le seul moyen fiable d'écouter les modifications de configuration est d'ignorer la méthode onConfigurationChanged() d'une vue, vous pouvez ajouter une nouvelle instance de vue à l'activité de votre application et répondre dans le gestionnaire onConfigurationChanged() de la vue aux modifications de configuration causées par l'association ou la dissociation du clavier.

Ingrédients

  • android:configChanges: attribut de l'élément <activity> du fichier manifeste de l'application. Informe le système des modifications de configuration gérées par l'application.
  • View#onConfigurationChanged(): méthode qui réagit à la propagation d'une nouvelle configuration d'application.

Étapes

Résumé

Déclarez l'attribut configChanges et ajoutez les valeurs associées au clavier. Ajoutez un View à la hiérarchie des vues de l'activité et écoutez les modifications de configuration.

1. Déclarer l'attribut configChanges

Mettez à jour l'élément <activity> dans le fichier manifeste de l'application en ajoutant les valeurs keyboard|keyboardHidden à la liste des modifications de configuration déjà gérées:

<activity
      …
      android:configChanges="...|keyboard|keyboardHidden">

2. Ajouter une vue vide à la hiérarchie des vues

Déclarez une nouvelle vue et ajoutez votre code de gestionnaire dans la méthode onConfigurationChanged() de la vue:

Kotlin

val v = object : View(this) {
  override fun onConfigurationChanged(newConfig: Configuration?) {
    super.onConfigurationChanged(newConfig)
    // Handler code here.
  }
}

Java

View v = new View(this) {
    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Handler code here.
    }
};

Résultats

Votre application répond désormais lorsqu'un clavier externe est connecté ou dissocié sans recréer l'activité en cours.

Ressources supplémentaires

Pour savoir comment enregistrer l'état de l'interface utilisateur de votre application lors de modifications de configuration telles que l'ajout ou la dissociation d'un clavier, consultez Enregistrer les états de l'interface utilisateur.