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. Możesz też zapoznać się z tymi informacjami:

  • Położenie 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, dlatego musisz je monitorować i wywoływać rekompozycję, gdy tylko nastąpi aktualizacja. Funkcja mediaQuery abstrahuje od szczegółów pobierania informacji i pozwala skupić się na definiowaniu warunku wywołującego aktualizacje układu.

.

W tym przykładzie układ przełącza się na TabletopLayout, gdy urządzenie jest w pozycji 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 funkcję lambda, która jest oceniana w ramach funkcji UiMediaScope. Funkcja mediaQuery ocenia stan na podstawie bieżącego stanu i możliwości urządzenia. Funkcja zwraca wartość logiczną, więc możesz określić układ za pomocą rozgałęzień 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 Obecna 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 aparat jest obsługiwany na urządzeniu.
hasMicrophone Boolean Określa, czy mikrofon jest obsługiwany na urządzeniu.
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 bieżące możliwości i kontekst urządzenia. Ten obiekt jest dynamicznie aktualizowany po wprowadzeniu jakichkolwiek zmian, np. gdy użytkownik zmieni położenie urządzenia. Funkcja mediaQuery ocenia następnie wyrażenie lambda query z zaktualizowanym obiektem UiMediaScope i zwraca wartość logiczną. Na przykład poniższy 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 opartych na opiniach punktów przerwania widocznego obszaru, które pomagają projektować, tworzyć i testować adaptacyjne układy. Możesz porównać 2 parametry reprezentujące bieżący rozmiar okna z wartością progową zdefiniowaną 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 wartości progowe klas rozmiarów okien (rysunek 1).

Funkcja derivedMediaQuery oblicza wartość wyrażenia lambda query i zawiera wynik w elemencie derivedStateOf. Ponieważ wartości parametrów windowWidth i windowHeight mogą się często zmieniać, w przypadku odwoływania się do tych parametrów w funkcji query lambda wywołuj 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 w zależności od położenia okna

Parametr windowPosture opisuje bieżące położenie okna jako obiekt UiMediaScope.Posture. Aktualny stan zabezpieczeń możesz sprawdzić, porównując parametr z wartościami zdefiniowanymi w klasie UiMediaScope.Posture. W tym przykładzie układ jest przełączany 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 dokładnie 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 największa w przypadku tych wartości, a najmniejsza w przypadku tych: Fine, Coarse i Blunt.

Jeśli dostępnych jest kilka urządzeń wskazujących o różnej precyzji, parametr jest rozwiązywany z użyciem najwyższej precyzji. Jeśli na przykład są 2 urządzenia wskazujące – Fine i 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 klawiatura sprzętowa jest dostępna w tym samym czasie, parametr jest rozwiązywany jako Physical. Jeśli nie zostanie wykryta żadna z tych wartości, parametrem będzie None. Poniższy przykład pokazuje komunikat sugerujący podłączenie klawiatury, gdy nie jest ona wykrywana:

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

Sprawdzanie, czy urządzenie obsługuje kamerę 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. Poniższy przykład pokazuje przyciski do użycia 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 na podstawie 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 zdefiniowane są 3 wartości: Near, Medium i Far. Near oznacza, że ekran jest w bliskiej odległości, 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

W funkcjach kompozycyjnych możesz wywoływać funkcje mediaQuery i derivedMediaQuery, aby wyświetlać podgląd komponentów interfejsu. Poniższy 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ą daną lambdę query w obiekcie UiMediaScope, który jest podawany jako LocalUiMediaScope.current. Możesz to zrobić, 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 treści lambda funkcji CompositionLocalProvider.

Podgląd elementu TabletopLayout możesz wyświetlić na tym przykładzie:

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