Informacje o zapytaniach dotyczące układów adaptacyjnych z mediaQuery

Aby zaktualizować układ aplikacji, potrzebujesz różnych informacji, takich jak możliwości urządzenia i stan aplikacji. Najczęściej używane informacje to szerokość i wysokość okna. Możesz też zapoznać się z tymi informacjami:

  • Pozycja okna
  • Precyzja urządzeń wskazujących
  • Typ klawiatury
  • Czy urządzenie obsługuje kamerę i mikrofon.
  • odległość między użytkownikiem a wyświetlaczem urządzenia,

Informacje są aktualizowane dynamicznie, więc musisz je monitorować i wywoływać rekompozycję po każdej aktualizacji. Funkcja mediaQuery abstrahuje szczegóły pobierania informacji i pozwala skupić się na definiowaniu warunku aktywującego aktualizacje układu.

W tym przykładzie układ zmienia się na TabletopLayout, gdy urządzenie składane jest w trybie Na stole:

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

Włącz funkcję mediaQuery

Aby włączyć funkcję mediaQuery, ustaw atrybut isMediaQueryIntegrationEnabled obiektu ComposeUiFlags na true:

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

Definiowanie warunku z parametrami

Warunek możesz zdefiniować jako funkcję lambda, która jest oceniana w ramach UiMediaScope. Funkcja mediaQuery ocenia warunek na podstawie bieżącego stanu i możliwości urządzenia. Funkcja zwraca wartość logiczną, dzięki czemu możesz określić układ za pomocą gałęzi warunkowych, takich jak wyrażenie if. Tabela 1 zawiera opis parametrów dostępnych w UiMediaScope.

Parametr Typ wartości Opis
windowWidth Dp Bieżąca szerokość okna w dp.
windowHeight Dp Bieżąca wysokość okna w dp.
windowPosture UiMediaScope.Posture Bieżący stan okna aplikacji.
pointerPrecision UiMediaScope.PointerPrecision Najwyższa precyzja spośród dostępnych urządzeń wskazujących.
keyboardKind UiMediaScope.KeyboardKind Typ dostępnej lub podłączonej klawiatury.
hasCamera Boolean Czy aparat jest obsługiwany na urządzeniu.
hasMicrophone Boolean Czy mikrofon jest obsługiwany na urządzeniu.
viewingDistance UiMediaScope.ViewingDistance Typowa odległość między użytkownikiem a ekranem urządzenia.

Obiekt UiMediaScope rozpoznaje wartości parametrów. Funkcja mediaQuery używa LocalUiMediaScope.current, aby uzyskać dostęp do obiektu UiMediaScope, który reprezentuje bieżące możliwości i kontekst urządzenia. Ten obiekt jest aktualizowany dynamicznie, gdy wprowadzane są zmiany, np. gdy użytkownik zmienia pozycję urządzenia. Funkcja mediaQuery ocenia następnie funkcję lambda query ze zaktualizowanym obiektem UiMediaScope i zwraca wartość logiczną. Na przykład ten fragment kodu wybiera między TabletopLayout a FlatLayout na podstawie wartości parametru windowPosture.

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

Podejmowanie decyzji na podstawie rozmiaru okna

Klasy rozmiaru okna to zestaw punktów przerwania widocznego obszaru, które pomagają projektować, tworzyć i testować układy adaptacyjne. Możesz porównać 2 parametry reprezentujące bieżący rozmiar okna z progiem zdefiniowanym w klasach rozmiaru okna. W poniższym przykładzie liczba paneli zmienia się w zależności od szerokości okna. Klasa WindowSizeClass zawiera stałe dla progów klas rozmiaru okna (rysunek 1).

Funkcja derivedMediaQuery ocenia funkcję lambda query i opakowuje wynik w derivedStateOf. Ponieważ parametry windowWidth i windowHeight mogą być często aktualizowane, gdy odwołujesz się do nich w funkcji lambda query, wywołaj funkcję derivedMediaQuery zamiast funkcji mediaQuery.

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

Rysunek 1. Układ jest aktualizowany zgodnie z szerokością okna.

Aktualizowanie układu zgodnie z położeniem okna

Parametr windowPosture opisuje bieżący układ okna jako obiekt UiMediaScope.Posture. Możesz sprawdzić bieżący stan, porównując parametr z wartościami zdefiniowanymi w klasie UiMediaScope.Posture. W tym przykładzie układ zmienia się w zależności od pozycji okna:

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

Sprawdzanie precyzji dostępnego urządzenia wskazującego

Urządzenie wskazujące o wysokiej precyzji pomaga użytkownikom precyzyjnie wskazywać elementy interfejsu. Precyzja urządzenia wskazującego zależy od jego typu.

Parametr pointerPrecision opisuje precyzję dostępnych urządzeń wskazujących, takich jak mysz i ekran dotykowy. W klasie UiMediaScope.PointerPrecision zdefiniowane są 4 wartości: Fine, Coarse, Blunt i None. None oznacza, że nie jest dostępne żadne urządzenie wskazujące. Precyzja jest podawana w kolejności od najwyższej do najniższej: Fine, Coarse i Blunt.

Jeśli dostępnych jest kilka urządzeń wskazujących o różnej precyzji, parametr jest określany na podstawie najwyższej z nich. Jeśli na przykład są 2 urządzenia wskazujące – jedno o precyzji Fine, a drugie o precyzji Blunt – wartością parametru pointerPrecision jest Fine.

Poniższy przykład pokazuje większy przycisk, gdy użytkownik korzysta z urządzenia wskazującego o niskiej precyzji:

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

Sprawdzanie dostępnego typu klawiatury

Parametr keyboardKind reprezentuje typ dostępnych klawiatur: Physical, Virtual i None. Jeśli klawiatura ekranowa jest wyświetlana, a jednocześnie dostępna jest klawiatura sprzętowa, parametr jest rozpoznawany jako Physical. Jeśli nie zostanie wykryty żaden z tych elementów, wartością parametru będzie None. Poniższy przykład pokazuje komunikat sugerujący użytkownikom podłączenie klawiatury, gdy nie zostanie ona wykryta:

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

Sprawdzanie, czy urządzenie obsługuje aparat i mikrofon

Niektóre urządzenia nie obsługują kamer ani mikrofonów. Możesz sprawdzić, czy urządzenie obsługuje kamerę i mikrofon, za pomocą parametrów hasCamera i hasMicrophone. W tym przykładzie pokazano przyciski, których można używać z kamerą i mikrofonem, gdy urządzenie je obsługuje:

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

Dostosowywanie interfejsu do szacowanej odległości oglądania

Odległość od ekranu jest czynnikiem, który pomaga określić układ. Jeśli użytkownik korzysta z aplikacji z pewnej odległości, będzie oczekiwać, że tekst i elementy interfejsu będą większe. Parametr viewingDistance podaje szacunkową odległość oglądania na podstawie typu urządzenia i typowego kontekstu jego użycia.

W klasie UiMediaScope.ViewingDistance zdefiniowane są 3 wartości: Near, Medium i Far. Near oznacza, że ekran jest blisko, a Far – że urządzenie jest oglądane z daleka. W tym przykładzie rozmiar czcionki zwiększa się, gdy odległość od ekranu wynosi Far lub Medium:

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

Wyświetlanie podglądu komponentu interfejsu

Aby wyświetlić podgląd komponentów interfejsu, możesz wywołać funkcje mediaQuery i derivedMediaQuery w funkcjach typu „composable”. Ten fragment kodu wybiera między TabletopLayout a FlatLayout na podstawie wartości parametru windowPosture. Aby wyświetlić podgląd TabletopLayout, parametr windowPosture powinien mieć wartość UiMediaScope.Posture.Tabletop.

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

Funkcje mediaQuery i derivedMediaQuery oceniają podaną funkcję lambda query w obiekcie UiMediaScope, który jest podany jako LocalUiMediaScope.current. Możesz je zastąpić, wykonując te czynności:

  1. Włącz funkcję mediaQuery.
  2. Zdefiniuj obiekt niestandardowy, który implementuje interfejs UiMediaScope.
  3. Ustaw obiekt niestandardowy na LocalUiMediaScope za pomocą funkcji CompositionLocalProvider.
  4. Wywołaj element kompozycyjny, aby wyświetlić podgląd w funkcji lambda treści funkcji CompositionLocalProvider.

Możesz wyświetlić podgląd TabletopLayout, korzystając z tego przykładu:

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