Informationen für adaptive Layouts mit mediaQuery abfragen

Sie benötigen verschiedene Arten von Informationen, z. B. die Gerätefunktionen und den App-Status, um das App-Layout zu aktualisieren. Die Fensterbreite und ‑höhe sind die am häufigsten verwendeten Informationen. Außerdem können Sie auf die folgenden Informationen verweisen:

  • Fensterposition
  • Genauigkeit von Zeigegeräten
  • Tastaturtyp
  • Ob Kamera und Mikrofon vom Gerät unterstützt werden
  • Der Abstand zwischen einem Nutzer und dem Gerätedisplay

Da die Informationen dynamisch aktualisiert werden, müssen Sie sie im Blick behalten und bei jeder Aktualisierung eine Neuzusammensetzung auslösen. Die mediaQuery Funktion abstrahiert die Details des Informationsabrufs und ermöglicht es Ihnen, sich auf die Definition der Bedingung zu konzentrieren, die die Layoutaktualisierungen auslöst. Im folgenden Beispiel wird das Layout auf TabletopLayout umgestellt, wenn die faltbare Position „Tischplatte“ ist:

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

Funktion mediaQuery aktivieren

Wenn Sie die mediaQuery Funktion aktivieren möchten, legen Sie das Attribut isMediaQueryIntegrationEnabled des Objekts ComposeUiFlags auf true fest:

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

Bedingung mit Parametern definieren

Sie können eine Bedingung als Lambda definieren, die in UiMediaScopeausgewertet wird. Die Funktion mediaQuery wertet die Bedingung entsprechend dem aktuellen Status und den Gerätefunktionen aus. Die Funktion gibt einen booleschen Wert zurück, sodass Sie das Layout mit bedingten Verzweigungen wie einem if-Ausdruck bestimmen können. In Tabelle 1 werden die in UiMediaScope verfügbaren Parameter beschrieben.

Parameter Werttyp Beschreibung
windowWidth Dp Die aktuelle Fensterbreite in dp.
windowHeight Dp Die aktuelle Fensterhöhe in dp.
windowPosture UiMediaScope.Posture Die aktuelle Position des Anwendungsfensters.
pointerPrecision UiMediaScope.PointerPrecision Die höchste Genauigkeit der verfügbaren Zeigegeräte.
keyboardKind UiMediaScope.KeyboardKind Der Typ der verfügbaren oder verbundenen Tastatur.
hasCamera Boolean Gibt an, ob die Kamera auf dem Gerät unterstützt wird.
hasMicrophone Boolean Gibt an, ob das Mikrofon auf dem Gerät unterstützt wird.
viewingDistance UiMediaScope.ViewingDistance Der typische Abstand zwischen dem Nutzer und dem Gerätedisplay.

Ein UiMediaScope-Objekt löst die Werte der Parameter auf. Die mediaQuery Funktion verwendet LocalUiMediaScope.current um auf das UiMediaScope Objekt zuzugreifen, das die aktuellen Gerätefunktionen und den aktuellen Kontext darstellt. Dieses Objekt wird dynamisch aktualisiert, wenn Änderungen vorgenommen werden, z. B. wenn der Nutzer die Geräteposition ändert. Die Funktion mediaQuery wertet dann das query-Lambda mit dem aktualisierten UiMediaScope-Objekt aus und gibt einen booleschen Wert zurück. Im folgenden Beispiel wird basierend auf dem Wert des Parameters windowPosture zwischen TabletopLayout und FlatLayout gewählt.

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

Entscheidung basierend auf der Fenstergröße treffen

Fenstergrößenklassen sind eine Reihe von Meinungen zu Viewport-Breakpoints die Ihnen beim Entwerfen, Entwickeln und Testen adaptiver Layouts helfen. Sie können die beiden Parameter, die die aktuelle Fenstergröße darstellen, mit dem in den Fenstergrößenklassen definierten Schwellenwert vergleichen. Im folgenden Beispiel wird die Anzahl der Bereiche entsprechend der Fensterbreite geändert. WindowSizeClass class has constants for the thresholds of window size classes (Figure 1).

Die derivedMediaQuery-Funktion wertet das query-Lambda aus und umschließt das Ergebnis in einem derivedStateOf. Da windowWidth und windowHeight häufig aktualisiert werden können, rufen Sie die Funktion derivedMediaQuery anstelle der Funktion mediaQuery auf, wenn Sie in der query-Lambda auf diese Parameter verweisen.

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()
}

Abbildung 1 : Das Layout wird entsprechend der Fensterbreite aktualisiert.

Layout entsprechend der Fensterposition aktualisieren

Der Parameter windowPosture beschreibt die aktuelle Fensterposition als UiMediaScope.Posture-Objekt. Sie können die aktuelle Position prüfen, indem Sie den Parameter mit den in der UiMediaScope.Posture Klasse definierten Werten vergleichen. Im folgenden Beispiel wird das Layout entsprechend der Fensterposition umgestellt:

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

Genauigkeit des verfügbaren Zeigegeräts prüfen

Ein Zeigegerät mit hoher Genauigkeit hilft Nutzern, ein UI-Element präzise anzusteuern. Die Genauigkeit eines Zeigegeräts hängt vom Gerätetyp ab.

Der Parameter pointerPrecision beschreibt die Genauigkeit der verfügbaren Zeigegeräte, z. B. einer Maus und eines Touchscreens. In der Klasse UiMediaScope.PointerPrecision sind vier Werte definiert: Fine, Coarse, Blunt und None. None bedeutet, dass kein Zeigegerät verfügbar ist. Die Genauigkeit reicht von der höchsten bis zur niedrigsten in dieser Reihenfolge: Fine, Coarse und Blunt.

Wenn mehrere Zeigegeräte verfügbar sind und sich ihre Genauigkeit unterscheidet, wird der Parameter mit der höchsten Genauigkeit aufgelöst. Wenn beispielsweise zwei Zeigegeräte vorhanden sind – ein Gerät mit Fine-Genauigkeit und ein Gerät mit Blunt-Genauigkeit – ist Fine der Wert des Parameters pointerPrecision.

Im folgenden Beispiel wird eine größere Schaltfläche angezeigt, wenn der Nutzer ein Zeigegerät mit geringer Genauigkeit verwendet:

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

Verfügbaren Tastaturtyp prüfen

Der keyboardKind Parameter stellt den Typ der verfügbaren Tastaturen dar: Physical, Virtual und None. Wenn eine Bildschirmtastatur angezeigt wird und gleichzeitig eine Hardwaretastatur verfügbar ist, wird der Parameter als Physical aufgelöst. Wenn keine der beiden Tastaturen erkannt wird, ist None der Wert des Parameters. Im folgenden Beispiel wird eine Meldung angezeigt, in der Nutzer aufgefordert werden, eine Tastatur anzuschließen, wenn keine Tastatur erkannt wird:

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

Prüfen, ob das Gerät Kamera und Mikrofon unterstützt

Einige Geräte unterstützen keine Kameras oder Mikrofone. Mit den Parametern hasCamera und hasMicrophone können Sie prüfen, ob das Gerät eine Kamera und ein Mikrofon unterstützt. Im folgenden Beispiel werden Schaltflächen für die Verwendung mit Kamera und Mikrofon angezeigt, wenn das Gerät diese unterstützt:

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()
    }
}

UI mit dem geschätzten Betrachtungsabstand anpassen

Der Betrachtungsabstand ist ein Faktor, der bei der Bestimmung des Layouts hilft. Wenn der Nutzer die App aus der Ferne verwendet, erwartet er, dass Text und UI-Elemente größer sind. Der Parameter viewingDistance liefert eine Schätzung des Betrachtungsabstands basierend auf dem Gerätetyp und dem typischen Nutzungskontext.

In der Klasse UiMediaScope.ViewingDistance sind drei Werte definiert: Near, Medium und Far. Near bedeutet, dass sich der Bildschirm in unmittelbarer Nähe befindet, und Far bedeutet, dass das Gerät aus der Ferne betrachtet wird. Im folgenden Beispiel wird die Schriftgröße erhöht, wenn der Betrachtungsabstand Far oder Medium ist:

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

UI-Komponente in der Vorschau ansehen

Sie können die Funktionen mediaQuery und derivedMediaQuery in den zusammensetzbaren Funktionen aufrufen, um UI-Komponenten in der Vorschau anzusehen. Im folgenden Beispiel wird basierend auf dem Wert des Parameters windowPosture zwischen TabletopLayout und FlatLayout gewählt. Wenn Sie TabletopLayout in der Vorschau ansehen möchten, muss der Parameter windowPosture UiMediaScope.Posture.Tabletop sein.

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

Die Funktionen mediaQuery und derivedMediaQuery werten das angegebene query Lambda in einem UiMediaScope Objekt aus, das als LocalUiMediaScope.current bereitgestellt wird. Sie können es mit den folgenden Schritten überschreiben:

  1. Funktion mediaQuery aktivieren.
  2. Definieren Sie ein benutzerdefiniertes Objekt, das die Schnittstelle UiMediaScope implementiert.
  3. Legen Sie das benutzerdefinierte Objekt mit der CompositionLocalProvider Funktion auf das LocalUiMediaScope fest.
  4. Rufen Sie die zusammensetzbare Funktion in der Content-Lambda der Funktion CompositionLocalProvider auf, um sie in der Vorschau anzusehen.

Mit dem folgenden Beispiel können Sie TabletopLayout in der Vorschau ansehen:

@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()
        }
    }
}