Informationen für adaptive Layouts mit mediaQuery abfragen

Sie benötigen verschiedene Arten von Informationen, z. B. zur Gerätefunktion und zum 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 sich die folgenden Informationen ansehen:

  • Fensterposition
  • Präzision 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 die Neuzusammenstellung auslösen, wenn eine Aktualisierung erfolgt. Die Funktion mediaQuery abstrahiert die Details des Abrufs von Informationen und ermöglicht es Ihnen, sich auf die Definition der Bedingung zum Auslösen der Layoutaktualisierungen zu konzentrieren.

Im folgenden Beispiel wird das Layout zu TabletopLayout gewechselt, wenn sich das faltbare Gerät im Tischmodus befindet:

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

Funktion „mediaQuery“ aktivieren

Um die Funktion mediaQuery zu aktivieren, setzen Sie das Attribut isMediaQueryIntegrationEnabled des ComposeUiFlags-Objekts auf true:

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 UiMediaScope ausgewertet wird. Die Funktion mediaQuery bewertet den Zustand anhand des aktuellen Status und der Gerätefunktionen. Die Funktion gibt einen booleschen Wert zurück. Sie können das Layout also mit bedingten Verzweigungen wie einem if-Ausdruck bestimmen. 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 Ausrichtung des Anwendungsfensters.
pointerPrecision UiMediaScope.PointerPrecision Die höchste Präzision 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ätebildschirm.

Ein UiMediaScope-Objekt löst die Werte der Parameter auf. Die Funktion mediaQuery verwendet LocalUiMediaScope.current, um auf das UiMediaScope-Objekt zuzugreifen, das die aktuellen Gerätefunktionen und den Kontext darstellt. Dieses Objekt wird dynamisch aktualisiert, wenn Änderungen vorgenommen werden, z. B. wenn der Nutzer die Geräteausrichtung ändert. Die Funktion mediaQuery wertet dann das Lambda query mit dem aktualisierten Objekt UiMediaScope aus und gibt einen booleschen Wert zurück. Im folgenden Beispiel wird basierend auf dem Parameterwert windowPosture zwischen TabletopLayout und FlatLayout ausgewä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 Ansichtsfenster-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. Die Klasse WindowSizeClass enthält Konstanten für die Grenzwerte der Fenstergrößenklassen (Abbildung 1).

Die Funktion derivedMediaQuery wertet das Lambda query aus und umschließt das Ergebnis mit einem derivedStateOf. Da windowWidth und windowHeight häufig aktualisiert werden können, sollten Sie die Funktion derivedMediaQuery anstelle der Funktion mediaQuery aufrufen, 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 anpassen

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

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

Prüfen der Genauigkeit des verfügbaren Eingabegeräts

Ein hochpräzises Zeigegerät hilft Nutzern, ein UI-Element genau zu positionieren. Die Präzision eines Zeigegeräts hängt vom Gerätetyp ab.

Der Parameter pointerPrecision beschreibt die Präzision der verfügbaren Zeigegeräte wie Maus und Touchscreen. 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 höchster bis niedrigster in dieser Reihenfolge: Fine, Coarse und Blunt.

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

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

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

Verfügbaren Tastaturtyp prüfen

Der Parameter keyboardKind steht für den Typ der verfügbaren Tastaturen: Physical, Virtual und None. Wenn eine Bildschirmtastatur angezeigt wird und gleichzeitig eine Hardwaretastatur verfügbar ist, wird der Parameter als Physical aufgelöst. Wenn keines der beiden 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. Das folgende Beispiel zeigt Schaltflächen für die Verwendung mit Kamera und Mikrofon, 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 an den 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, dass das Gerät aus der Ferne betrachtet wird. Im folgenden Beispiel wird die Schriftgröße erhöht, wenn der Betrachtungsabstand Far oder Medium beträgt:

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

Vorschau einer UI-Komponente ansehen

Sie können die Funktionen mediaQuery und derivedMediaQuery in den zusammensetzbaren Funktionen aufrufen, um eine Vorschau von UI-Komponenten zu sehen. Im folgenden Snippet wird basierend auf dem Parameterwert windowPosture zwischen TabletopLayout und FlatLayout ausgewählt. Wenn Sie eine Vorschau von TabletopLayout sehen möchten, sollte der Parameter windowPosture auf UiMediaScope.Posture.Tabletop festgelegt sein.

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

Mit den Funktionen mediaQuery und derivedMediaQuery wird das angegebene Lambda query in einem UiMediaScope-Objekt ausgewertet, das als LocalUiMediaScope.current bereitgestellt wird. So können Sie die Einstellung überschreiben:

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

Sie können sich die TabletopLayout mit dem folgenden Beispiel 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()
        }
    }
}