Necesitas varios tipos de información, como la capacidad del dispositivo y el estado de la app, para actualizar el diseño de tu app. El ancho y la altura de la ventana son la información de uso frecuente. Además, puedes consultar la siguiente información:
- Postura de la ventana
- Precisión de los dispositivos apuntadores
- Tipo de teclado
- Si el dispositivo admite la cámara y el micrófono
- La distancia entre un usuario y la pantalla del dispositivo
Debido a que la información se actualiza de forma dinámica, debes supervisarla y activar la recomposición cuando se produzca una actualización.
La función mediaQuery abstrae los detalles de la recuperación de información y te permite enfocarte en definir la condición para activar las actualizaciones de diseño.
En el siguiente ejemplo, se cambia el diseño a TabletopLayout cuando la postura del plegable es de mesa:
@Composable fun VideoPlayer( // ... ) { // ... if (mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop }) { TabletopLayout() } else { FlatLayout() } // ... }
Habilita la función mediaQuery
Para habilitar la función mediaQuery,
establece el atributo isMediaQueryIntegrationEnabled del
objeto ComposeUiFlags en true:
class MyApplication : Application() { override fun onCreate() { ComposeUiFlags.isMediaQueryIntegrationEnabled = true super.onCreate() } }
Define una condición con parámetros
Puedes definir una condición como una expresión lambda que se evalúa dentro de UiMediaScope.
La función mediaQuery evalúa la condición según
el estado actual y las capacidades del dispositivo.
La función devuelve un valor booleano,
por lo que puedes determinar el diseño con ramas condicionales,
como una expresión if.
En la Tabla 1, se describen los parámetros disponibles en UiMediaScope.
| Parámetro | Tipo de valor | Descripción |
|---|---|---|
windowWidth |
Dp |
Es el ancho actual de la ventana en dp. |
windowHeight |
Dp |
Es la altura actual de la ventana en dp. |
windowPosture |
UiMediaScope.Posture |
La postura actual de la ventana de la aplicación. |
pointerPrecision |
UiMediaScope.PointerPrecision |
Es la precisión más alta de los dispositivos apuntadores disponibles. |
keyboardKind |
UiMediaScope.KeyboardKind |
Es el tipo de teclado disponible o conectado. |
hasCamera |
Boolean |
Indica si la cámara es compatible con el dispositivo. |
hasMicrophone |
Boolean |
Indica si el micrófono es compatible con el dispositivo. |
viewingDistance |
UiMediaScope.ViewingDistance |
Es la distancia típica entre el usuario y la pantalla del dispositivo. |
Un objeto UiMediaScope resuelve los valores de los parámetros.
La función mediaQuery usa LocalUiMediaScope.current para acceder al objeto UiMediaScope, que representa las capacidades y el contexto actuales del dispositivo.
Este objeto se actualiza de forma dinámica cuando se realizan cambios,
como cuando el usuario cambia la postura del dispositivo.
Luego, la función mediaQuery evalúa la expresión lambda query con el objeto UiMediaScope actualizado y devuelve un valor booleano.
Por ejemplo, el siguiente fragmento elige entre TabletopLayout
y FlatLayout según el valor del parámetro windowPosture.
@Composable fun VideoPlayer( // ... ) { // ... if (mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop }) { TabletopLayout() } else { FlatLayout() } // ... }
Toma una decisión según el tamaño de la ventana
Las clases de tamaño de ventana son un conjunto de puntos de interrupción de viewports bien definidos que te ayudan a diseñar, desarrollar y probar diseños adaptables.
Puedes comparar los dos parámetros que representan el tamaño actual de la ventana con el umbral definido en las clases de tamaño de ventana.
En el siguiente ejemplo, se cambia la cantidad de paneles según el ancho de la ventana.
La clase WindowSizeClass tiene constantes para los umbrales de las clases de tamaño de ventana (figura 1).
La función derivedMediaQuery evalúa la lambda query y une el resultado en un derivedStateOf.
Como windowWidth y windowHeight se pueden actualizar con frecuencia, llama a la función derivedMediaQuery en lugar de la función mediaQuery cuando hagas referencia a esos parámetros en la lambda query.
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() }
Actualiza el diseño según la postura de la ventana
El parámetro windowPosture describe la postura actual de la ventana como un objeto UiMediaScope.Posture.
Puedes verificar la postura actual comparando el parámetro con los valores definidos en la clase UiMediaScope.Posture.
En el siguiente ejemplo, se cambia el diseño según la postura de la ventana:
when { mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop } -> TabletopLayout() mediaQuery { windowPosture == UiMediaScope.Posture.Book } -> BookLayout() mediaQuery { windowPosture == UiMediaScope.Posture.Flat } -> FlatLayout() }
Verifica la precisión del dispositivo apuntador disponible
Un dispositivo apuntador de alta precisión ayuda a los usuarios a apuntar a un elemento de la IU con precisión. La precisión de un dispositivo apuntador depende del tipo de dispositivo.
El parámetro pointerPrecision describe la precisión
de los dispositivos apuntadores disponibles, como un mouse y una pantalla táctil.
Hay cuatro valores definidos en la clase UiMediaScope.PointerPrecision:
Fine, Coarse, Blunt y None.
None significa que no hay ningún dispositivo apuntador disponible.
La precisión varía de la más alta a la más baja en este orden:
Fine, Coarse y Blunt.
Si hay varios dispositivos apuntadores disponibles y sus precisiones son diferentes, el parámetro se resuelve con el más alto.
Por ejemplo, si hay dos dispositivos apuntadores, uno de precisión Fine y otro de precisión Blunt, Fine es el valor del parámetro pointerPrecision.
En el siguiente ejemplo, se muestra un botón más grande cuando el usuario utiliza un dispositivo apuntador con baja precisión:
if (mediaQuery { pointerPrecision == UiMediaScope.PointerPrecision.Blunt }) { LargeSizeButton() } else { NormalSizeButton() }
Verifica el tipo de teclado disponible
El parámetro keyboardKind representa el tipo de teclados disponibles: Physical, Virtual y None.
Si se muestra un teclado en pantalla y, al mismo tiempo, hay un teclado de hardware disponible, el parámetro se resuelve como Physical.
Si no se detecta ninguno, None es el valor del parámetro.
En el siguiente ejemplo, se muestra un mensaje que sugiere que los usuarios conecten un teclado cuando no se detecta ninguno:
if (mediaQuery { keyboardKind == UiMediaScope.KeyboardKind.None }) { SuggestKeyboardConnect() }
Comprueba si el dispositivo admite la cámara y el micrófono
Algunos dispositivos no admiten cámaras ni micrófonos.
Puedes verificar si el dispositivo admite una cámara y un micrófono con los parámetros hasCamera y hasMicrophone.
En el siguiente ejemplo, se muestran los botones que se deben usar con la cámara y el micrófono cuando el dispositivo los admite:
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() } }
Ajusta la IU con la distancia de visualización estimada
La distancia de visualización es un factor que ayuda a determinar el diseño.
Si el usuario usa la app desde lejos,
esperará que el texto y los elementos de la IU sean más grandes.
El parámetro viewingDistance proporciona una estimación de la distancia de visualización según el tipo de dispositivo y su contexto de uso típico.
Hay tres valores definidos en la clase UiMediaScope.ViewingDistance:
Near, Medium y Far.
Near significa que la pantalla está cerca,
y Far significa que el dispositivo se ve desde lejos.
En el siguiente ejemplo, se aumenta el tamaño de la fuente cuando la distancia de visualización es de Far o Medium:
val fontSize = when { mediaQuery { viewingDistance == UiMediaScope.ViewingDistance.Far } -> 20.sp mediaQuery { viewingDistance == UiMediaScope.ViewingDistance.Medium } -> 18.sp else -> 16.sp }
Obtén una vista previa de un componente de la IU
Puedes llamar a las funciones mediaQuery y derivedMediaQuery en las funciones de componibilidad para obtener una vista previa de los componentes de la IU.
En el siguiente fragmento, se elige entre TabletopLayout
y FlatLayout según el valor del parámetro windowPosture.
Para obtener una vista previa de TabletopLayout, el parámetro windowPosture debe ser
UiMediaScope.Posture.Tabletop.
when { mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop } -> TabletopLayout() mediaQuery { windowPosture == UiMediaScope.Posture.Book } -> BookLayout() mediaQuery { windowPosture == UiMediaScope.Posture.Flat } -> FlatLayout() }
Las funciones mediaQuery y derivedMediaQuery evalúan
la expresión lambda query dada dentro de un objeto UiMediaScope,
que se proporciona como LocalUiMediaScope.current.
Puedes anularlo con los siguientes pasos:
- Habilita la función
mediaQuery. - Define un objeto personalizado que implemente la interfaz
UiMediaScope. - Establece el objeto personalizado en
LocalUiMediaScopecon la funciónCompositionLocalProvider. - Llama a la función de componibilidad para obtener una vista previa en la lambda de contenido de la función
CompositionLocalProvider.
Puedes obtener una vista previa de TabletopLayout con el siguiente ejemplo:
@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() } } }