Interroger des informations pour les mises en page adaptatives avec mediaQuery

Vous avez besoin de différents types d'informations, comme les fonctionnalités de l'appareil et l'état de l'application, pour mettre à jour la mise en page de votre application. La largeur et la hauteur de la fenêtre sont les informations les plus couramment utilisées. Vous pouvez également consulter les informations suivantes :

  • Position de la fenêtre
  • Précision des dispositifs de pointage
  • Type de clavier
  • Si la caméra et le micro sont compatibles avec l'appareil
  • La distance entre un utilisateur et l'écran de l'appareil

Étant donné que les informations sont mises à jour de manière dynamique, vous devez les surveiller et déclencher la recomposition en cas de mise à jour. La fonction mediaQuery résume les détails de la récupération d'informations et vous permet de vous concentrer sur la définition de la condition pour déclencher les mises à jour de la mise en page. L'exemple suivant bascule la mise en page sur TabletopLayout lorsque l'appareil pliable est en position table :

@Composable
fun VideoPlayer(
    // ...
) {
    // ...
            if (mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop }) {
                TabletopLayout()
            } else {
                FlatLayout()
            }
    // ...
}

Activer la fonction mediaQuery

Pour activer la fonction mediaQuery, définissez l'attribut isMediaQueryIntegrationEnabled de l'objet ComposeUiFlags sur true :

class MyApplication : Application() {
    override fun onCreate() {
        ComposeUiFlags.isMediaQueryIntegrationEnabled = true
        super.onCreate()
    }
}

Définir une condition avec des paramètres

Vous pouvez définir une condition en tant que lambda évalué dans UiMediaScope. La fonction mediaQuery évalue la condition en fonction de l'état actuel et des capacités de l'appareil. La fonction renvoie une valeur booléenne. Vous pouvez donc déterminer la mise en page avec des branches conditionnelles comme une expression if. Le tableau 1 décrit les paramètres disponibles dans UiMediaScope.

Paramètre Type de valeur Description
windowWidth Dp Largeur actuelle de la fenêtre en dp.
windowHeight Dp Hauteur actuelle de la fenêtre en dp.
windowPosture UiMediaScope.Posture Posture actuelle de la fenêtre de l'application.
pointerPrecision UiMediaScope.PointerPrecision Précision la plus élevée des dispositifs de pointage disponibles.
keyboardKind UiMediaScope.KeyboardKind Type de clavier disponible ou connecté.
hasCamera Boolean Indique si l'appareil photo est compatible avec l'appareil.
hasMicrophone Boolean Indique si le micro est pris en charge sur l'appareil.
viewingDistance UiMediaScope.ViewingDistance Distance habituelle entre l'utilisateur et l'écran de l'appareil.

Un objet UiMediaScope résout les valeurs des paramètres. La fonction mediaQuery utilise LocalUiMediaScope.current pour accéder à l'objet UiMediaScope, qui représente les capacités et le contexte actuels de l'appareil. Cet objet est mis à jour de manière dynamique lorsque des modifications sont apportées, par exemple lorsque l'utilisateur modifie la position de l'appareil. La fonction mediaQuery évalue ensuite le lambda query avec l'objet UiMediaScope mis à jour et renvoie une valeur booléenne. Par exemple, l'extrait suivant choisit entre TabletopLayout et FlatLayout en fonction de la valeur du paramètre windowPosture.

@Composable
fun VideoPlayer(
    // ...
) {
    // ...
            if (mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop }) {
                TabletopLayout()
            } else {
                FlatLayout()
            }
    // ...
}

Prendre une décision en fonction de la taille de la fenêtre

Les classes de taille de fenêtre sont un ensemble de points d'arrêt de fenêtre d'affichage définis qui permettent de concevoir, de développer et de tester des mises en page adaptatives. Vous pouvez comparer les deux paramètres représentant la taille actuelle de la fenêtre avec le seuil défini dans les classes de taille de fenêtre. L'exemple suivant modifie le nombre de volets en fonction de la largeur de la fenêtre. La classe WindowSizeClass comporte des constantes pour les seuils des classes de taille de fenêtre (figure 1).

La fonction derivedMediaQuery évalue le lambda query et encapsule le résultat dans un derivedStateOf. Étant donné que windowWidth et windowHeight peuvent être mis à jour fréquemment, appelez la fonction derivedMediaQuery au lieu de la fonction mediaQuery lorsque vous faites référence à ces paramètres dans le lambda query.

val narrowerThanMedium by derivedMediaQuery {
    windowWidth < WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND.dp
}
val narrowerThanExpanded by derivedMediaQuery {
    windowWidth < WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND.dp
}
when {
    narrowerThanMedium -> SinglePaneLayout()
    narrowerThanExpanded -> TwoPaneLayout()
    else -> ThreePaneLayout()
}

Figure 1. La mise en page est mise à jour en fonction de la largeur de la fenêtre.

Mettre à jour la mise en page en fonction de la position de la fenêtre

Le paramètre windowPosture décrit la posture actuelle de la fenêtre sous la forme d'un objet UiMediaScope.Posture. Vous pouvez vérifier la posture actuelle en comparant le paramètre aux valeurs définies dans la classe UiMediaScope.Posture. L'exemple suivant change de mise en page en fonction de la position de la fenêtre :

when {
    mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop } -> TabletopLayout()
    mediaQuery { windowPosture == UiMediaScope.Posture.Book } -> BookLayout()
    mediaQuery { windowPosture == UiMediaScope.Posture.Flat } -> FlatLayout()
}

Vérifier la précision du dispositif de pointage disponible

Un dispositif de pointage de haute précision aide les utilisateurs à pointer précisément un élément d'interface utilisateur. La précision d'un dispositif de pointage dépend du type d'appareil.

Le paramètre pointerPrecision décrit la précision des dispositifs de pointage disponibles, tels qu'une souris et un écran tactile. Quatre valeurs sont définies dans la classe UiMediaScope.PointerPrecision : Fine, Coarse, Blunt et None. None signifie qu'aucun dispositif de pointage n'est disponible. La précision est classée de la plus élevée à la plus faible dans l'ordre suivant : Fine, Coarse et Blunt.

Si plusieurs dispositifs de pointage sont disponibles et que leur précision est différente, le paramètre est résolu avec la précision la plus élevée. Par exemple, s'il existe deux dispositifs de pointage (un dispositif de précision Fine et un dispositif de précision Blunt), Fine est la valeur du paramètre pointerPrecision.

L'exemple suivant montre un bouton plus grand lorsque l'utilisateur utilise un dispositif de pointage à faible précision :

if (mediaQuery { pointerPrecision == UiMediaScope.PointerPrecision.Blunt }) {
    LargeSizeButton()
} else {
    NormalSizeButton()
}

Vérifier le type de clavier disponible

Le paramètre keyboardKind représente le type de claviers disponibles : Physical, Virtual et None. Si un clavier à l'écran est affiché et qu'un clavier physique est disponible en même temps, le paramètre est résolu en tant que Physical. Si aucune n'est détectée, None est la valeur du paramètre. L'exemple suivant montre un message suggérant aux utilisateurs de connecter un clavier lorsqu'aucun clavier n'est détecté :

if (mediaQuery { keyboardKind == UiMediaScope.KeyboardKind.None }) {
    SuggestKeyboardConnect()
}

Vérifier si l'appareil est compatible avec la caméra et le micro

Certains appareils ne sont pas compatibles avec les caméras ni les micros. Vous pouvez vérifier si l'appareil est équipé d'une caméra et d'un micro à l'aide des paramètres hasCamera et hasMicrophone. L'exemple suivant montre les boutons à utiliser avec l'appareil photo et le micro lorsque l'appareil les prend en charge :

Row {
    OutlinedTextField(state = rememberTextFieldState())
    // Show the MicButton when the device supports a microphone.
    if (mediaQuery { hasMicrophone }) {
        MicButton()
    }
    // Show the CameraButton when the device supports a camera.
    if (mediaQuery { hasCamera }) {
        CameraButton()
    }
}

Ajuster l'UI en fonction de la distance de visionnage estimée

La distance de visionnage est un facteur qui permet de déterminer la mise en page. Si l'utilisateur utilise l'application à distance, il s'attend à ce que le texte et les éléments d'interface utilisateur soient plus grands. Le paramètre viewingDistance fournit une estimation de la distance de visionnage en fonction du type d'appareil et de son contexte d'utilisation habituel.

Trois valeurs sont définies dans la classe UiMediaScope.ViewingDistance : Near, Medium et Far. Near signifie que l'écran est à portée de main, et Far signifie que l'appareil est vu de loin. L'exemple suivant augmente la taille de la police lorsque la distance de visionnage est Far ou Medium :

val fontSize = when {
    mediaQuery { viewingDistance == UiMediaScope.ViewingDistance.Far } -> 20.sp
    mediaQuery { viewingDistance == UiMediaScope.ViewingDistance.Medium } -> 18.sp
    else -> 16.sp
}

Prévisualiser un composant d'UI

Vous pouvez appeler les fonctions mediaQuery et derivedMediaQuery dans les fonctions composables pour prévisualiser les composants de l'UI. L'extrait suivant choisit entre TabletopLayout et FlatLayout en fonction de la valeur du paramètre windowPosture. Pour prévisualiser le TabletopLayout, le paramètre windowPosture doit être défini sur UiMediaScope.Posture.Tabletop.

when {
    mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop } -> TabletopLayout()
    mediaQuery { windowPosture == UiMediaScope.Posture.Book } -> BookLayout()
    mediaQuery { windowPosture == UiMediaScope.Posture.Flat } -> FlatLayout()
}

Les fonctions mediaQuery et derivedMediaQuery évaluent le lambda query donné dans un objet UiMediaScope, qui est fourni en tant que LocalUiMediaScope.current. Pour le remplacer, procédez comme suit :

  1. Activez la fonction mediaQuery.
  2. Définissez un objet personnalisé qui implémente l'interface UiMediaScope.
  3. Définissez l'objet personnalisé sur LocalUiMediaScope avec la fonction CompositionLocalProvider.
  4. Appelez le composable à prévisualiser dans le lambda de contenu de la fonction CompositionLocalProvider.

Vous pouvez prévisualiser TabletopLayout avec l'exemple suivant :

@Preview
@Composable
fun PreviewLayoutForTabletop() {
    // Step 1: Enable the mediaQuery function
    ComposeUiFlags.isMediaQueryIntegrationEnabled = true

    val currentUiMediaScope = LocalUiMediaScope.current
    // Step 2: Define a custom object implementing the UiMediaScope interface.
    // The object overrides the windowPosture parameter.
    // The resolution of the remaining parameters is deferred to the currentUiMediaScope object.
    val uiMediaScope = remember(currentUiMediaScope) {
        object : UiMediaScope by currentUiMediaScope {
            override val windowPosture: UiMediaScope.Posture = UiMediaScope.Posture.Tabletop
        }
    }

    // Step 3: Set the object to the LocalUiMediaScope.
    CompositionLocalProvider(LocalUiMediaScope provides uiMediaScope) {
        // Step 4: Call the composable to preview.
        when {
            mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop } -> TabletopLayout()
            mediaQuery { windowPosture == UiMediaScope.Posture.Book } -> BookLayout()
            mediaQuery { windowPosture == UiMediaScope.Posture.Flat } -> FlatLayout()
        }
    }
}