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 que se usa con mayor frecuencia. Además, puedes consultar la siguiente información:
- Posición de la ventana
- Precisión de los dispositivos apuntadores
- Tipo de teclado
- Indica 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 alguna 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 del diseño.
En el siguiente ejemplo, se cambia el diseño a TabletopLayout cuando la postura del dispositivo 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, configura el atributo isMediaQueryIntegrationEnabled del objeto ComposeUiFlags en true:
class MyApplication : Application() { override fun onCreate() { ComposeUiFlags.isMediaQueryIntegrationEnabled = true super.onCreate() } }
Cómo definir 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 |
Ancho de la ventana actual en dp. |
windowHeight |
Dp |
Es la altura actual de la ventana en dp. |
windowPosture |
UiMediaScope.Posture |
Es la postura actual de la ventana de la aplicación. |
pointerPrecision |
UiMediaScope.PointerPrecision |
Es la mayor precisión de los dispositivos de puntero 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, por ejemplo, cuando el usuario cambia la posición 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 de ventana actual 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 expresión lambda query y encapsula el resultado en un derivedStateOf.
Debido a que windowWidth y windowHeight pueden actualizarse con frecuencia, llama a la función derivedMediaQuery en lugar de a la función mediaQuery cuando hagas referencia a esos parámetros en la expresión 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 posición 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 posición 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 de puntero disponible
Un dispositivo de puntero 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 de puntero 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 mayor a menor en este orden: Fine, Coarse y Blunt.
Si hay varios dispositivos de puntero disponibles y sus precisiones son diferentes, el parámetro se resuelve con la precisión más alta.
Por ejemplo, si hay dos dispositivos de puntero, 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 usa un dispositivo apuntador con baja precisión:
if (mediaQuery { pointerPrecision == UiMediaScope.PointerPrecision.Blunt }) { LargeSizeButton() } else { NormalSizeButton() }
Cómo verificar 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 hay un teclado de hardware disponible al mismo tiempo, 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() }
Cómo verificar 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 pueden 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ía 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á a corta distancia, 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 Far o Medium:
val fontSize = when { mediaQuery { viewingDistance == UiMediaScope.ViewingDistance.Far } -> 20.sp mediaQuery { viewingDistance == UiMediaScope.ViewingDistance.Medium } -> 18.sp else -> 16.sp }
Cómo obtener 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.
El siguiente fragmento 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 proporcionada 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 al elemento componible para obtener una vista previa en la expresión lambda de contenido de la función
CompositionLocalProvider.
Puedes obtener una vista previa del 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() } } }