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:

  • Położenie okna
  • Precyzja urządzeń wskazujących
  • Typ klawiatury
  • Czy aparat i mikrofon są obsługiwane przez urządzenie.
  • odległość między użytkownikiem a wyświetlaczem urządzenia,

Informacje są aktualizowane dynamicznie, dlatego musisz je monitorować i wywoływać ponowne komponowanie, 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 poniższym przykładzie układ przełącza się na TabletopLayout, gdy urządzenie składane jest w pozycji 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()
    }
}

Określanie warunku za pomocą parametrów

Warunek możesz zdefiniować jako funkcję lambda, która jest oceniana w UiMediaScope. Funkcja mediaQuery ocenia warunek 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 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 do uzyskiwania dostępu 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 oblicza następnie wyrażenie lambda query z zaktualizowanym obiektem UiMediaScope i zwraca wartość logiczną. Na przykład ten fragment kodu wybiera wartość TabletopLayout lub 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ć układy adaptacyjne. Możesz porównać 2 parametry reprezentujące bieżący rozmiar okna z wartością progową zdefiniowaną w klasach rozmiarów okna. W tym przykładzie liczba paneli zmienia się w zależności od szerokości okna. Klasa WindowSizeClass zawiera stałe wartości progowe klas rozmiaru okna (rysunek 1).

Funkcja derivedMediaQuery oblicza wartość wyrażenia lambda query i zawiera wynik w funkcji derivedStateOf. Ponieważ funkcje windowWidthwindowHeight mogą być często aktualizowane, 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 aktualną pozycję okna jako obiekt UiMediaScope.Posture. Aktualną postawę możesz sprawdzić, 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 orientacji 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, BluntNone. None oznacza, że nie ma dostępnego urządzenia wskazującego. Precyzja jest największa w przypadku tych wartości, a najmniejsza w przypadku tych: Fine, CoarseBlunt.

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, VirtualNone. Jeśli klawiatura ekranowa jest wyświetlana, a klawiatura sprzętowa jest dostępna w tym samym czasie, parametr jest rozpoznawany jako Physical. Jeśli nie zostanie wykryta żadna z tych wartości, parametrem będzie None. Poniżej znajdziesz przykład komunikatu sugerującego podłączenie klawiatury, gdy nie jest ona wykrywana:

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

Sprawdź, 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 hasCamerahasMicrophone. Ten 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, MediumFar. Near oznacza, że ekran jest w pobliżu, a Far oznacza, że urządzenie jest oglądane z daleka. W tym przykładzie zwiększamy rozmiar czcionki, 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 mediaQueryderivedMediaQuery, aby wyświetlać podgląd komponentów interfejsu. Poniższy fragment kodu wybiera między TabletopLayoutFlatLayout 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ą daną funkcję lambda query w obiekcie UiMediaScope, który jest podany jako LocalUiMediaScope.current. Możesz ją 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 treści lambda funkcji CompositionLocalProvider.

Podgląd atrybutu TabletopLayout możesz zobaczyć 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()
        }
    }
}