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

Aby zaktualizować układ aplikacji, potrzebujesz różnych informacji, np. o możliwościach urządzenia i stanie aplikacji. Najczęściej używane informacje to szerokość i wysokość okna. Oprócz tego możesz korzystać z tych informacji:

  • pozycja okna,
  • precyzja urządzeń wskazujących,
  • typ klawiatury,
  • czy urządzenie obsługuje aparat i mikrofon,
  • odległość między użytkownikiem a ekranem urządzenia.

Informacje są aktualizowane dynamicznie, dlatego musisz je monitorować i uruchamiać ponowne komponowanie, gdy nastąpi aktualizacja. Funkcja mediaQuery abstrahuje szczegóły pobierania informacji i pozwala skupić się na definiowaniu warunku uruchamiającego aktualizacje układu.

Ten przykład przełącza układ na TabletopLayout, gdy pozycja składanego urządzenia jest ustawiona na stole:

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

Włączanie funkcji 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 lambdę która jest obliczana w UiMediaScope. Funkcja mediaQuery oblicza 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 Aktualna szerokość okna w dp.
windowHeight Dp Aktualna wysokość okna w dp.
windowPosture UiMediaScope.Posture Aktualna pozycja okna aplikacji.
pointerPrecision UiMediaScope.PointerPrecision Najwyższa precyzja dostępnych urządzeń wskazujących.
keyboardKind UiMediaScope.KeyboardKind Typ dostępnej lub podłączonej klawiatury.
hasCamera Boolean Czy urządzenie obsługuje aparat.
hasMicrophone Boolean Czy urządzenie obsługuje mikrofon.
viewingDistance UiMediaScope.ViewingDistance Typowa odległość między użytkownikiem a ekranem urządzenia.

Obiekt UiMediaScope rozwiązuje wartości parametrów. Funkcja mediaQuery używa LocalUiMediaScope.current aby uzyskać dostęp do obiektu UiMediaScope, który reprezentuje aktualne możliwości i kontekst urządzenia. Ten obiekt jest dynamicznie aktualizowany, gdy wprowadzane są zmiany, np. gdy użytkownik zmienia pozycję urządzenia. Funkcja mediaQuery oblicza następnie lambdę query za pomocą zaktualizowanego obiektu 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 rozmiarów okien to zestaw opinii na temat punktów przerwania widocznego obszaru które pomagają projektować, tworzyć i testować układy adaptacyjne. Możesz porównać 2 parametry reprezentujące aktualny rozmiar okna z progiem zdefiniowanym w klasach rozmiarów okien. Ten przykład zmienia liczbę paneli w zależności od szerokości okna. WindowSizeClass klasa ma stałe wartości progowe klas rozmiarów okien (rysunek 1).

Funkcja derivedMediaQuery oblicza lambdę query i opakowuje wynik w derivedStateOf. Ponieważ windowWidth i windowHeight mogą się często zmieniać, w lambdzie query odwołuj się do tych parametrów za pomocą funkcji derivedMediaQuery zamiast 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 w zależności od szerokości okna.

Aktualizowanie układu w zależności od pozycji okna

Parametr windowPosture opisuje aktualną pozycję okna jako obiekt UiMediaScope.Posture. Aktualną pozycję możesz sprawdzić, porównując parametr z wartościami zdefiniowanymi w klasie UiMediaScope.Posture. Ten przykład przełącza układ 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 zdefiniowano 4 wartości: Fine, Coarse, Blunt i None. None oznacza, że nie jest dostępne żadne urządzenie wskazujące. Precyzja maleje w tej kolejności: Fine, Coarse i Blunt.

Jeśli dostępnych jest kilka urządzeń wskazujących o różnej precyzji, parametr jest rozwiązywany z najwyższą precyzją. Jeśli na przykład dostępne są 2 urządzenia wskazujące – jedno o precyzji Fine i jedno o precyzji Blunt – wartością parametru pointerPrecision będzie Fine.

Ten przykład pokazuje większy przycisk, gdy użytkownik używa 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 jednocześnie wyświetlana jest klawiatura ekranowa i dostępna jest klawiatura sprzętowa, parametr jest rozwiązywany jako Physical. Jeśli nie wykryto żadnej z nich, wartością parametru jest None. Ten przykład pokazuje komunikat sugerujący podłączenie klawiatury, gdy nie zostanie wykryta żadna klawiatura:

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

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

Niektóre urządzenia nie obsługują aparatów ani mikrofonów. Możesz sprawdzić, czy urządzenie obsługuje aparat i mikrofon, za pomocą parametrów hasCamera i hasMicrophone. Ten przykład pokazuje przyciski do użycia z aparatem 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ść oglądania to czynnik, który pomaga określić układ. Jeśli użytkownik korzysta z aplikacji z daleka, oczekuje, że tekst i elementy interfejsu będą większe. Parametr viewingDistance zawiera szacunkową odległość oglądania na podstawie typu urządzenia i typowego kontekstu jego użycia.

W klasie UiMediaScope.ViewingDistance zdefiniowano 3 wartości: Near, Medium i Far. Near oznacza, że ekran znajduje się w bliskiej odległości, a Far – że urządzenie jest oglądane z daleka. Ten przykład zwiększa rozmiar czcionki, gdy odległość oglądania jest równa 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 kompozycyjnych. 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 obliczają podaną lambdę query w obiekcie UiMediaScope, który jest udostępniany jako LocalUiMediaScope.current. Możesz go 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 funkcję kompozycyjną, aby wyświetlić podgląd w lambdzie treści funkcji CompositionLocalProvider.

Podgląd TabletopLayout możesz wyświetlić za pomocą 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()
        }
    }
}