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() }
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:
- Włącz funkcję
mediaQuery. - Zdefiniuj obiekt niestandardowy, który implementuje interfejs
UiMediaScope. - Ustaw obiekt niestandardowy na
LocalUiMediaScopeza pomocą funkcjiCompositionLocalProvider. - 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() } } }