Window size classes: medir el espacio, no adivinar el aparato
Las clases de tamaño de ventana son el punto de corte que convierte una magnitud continua e inestable —los puntos de ancho y alto que el gestor de ventanas te concede ahora mismo— en un puñado de categorías estables sobre las que se puede decidir estructura. Esta lección explica de dónde salen los umbrales, por qué se miden sobre la ventana y no sobre la pantalla, cómo se obtienen y observan en Compose, qué hace la ampliación reciente del catálogo con las tallas grande y extragrande, y sobre todo qué tipo de decisiones pertenecen a esta abstracción y cuáles no.
El espacio disponible es una magnitud continua: cualquier número de puntos entre el ancho mínimo que el sistema garantiza y el de un monitor externo. Programar contra un continuo es inviable, porque cada valor exigiría una decisión y ninguna sería verificable. Las clases de tamaño de ventana resuelven ese problema con la operación más antigua de la ingeniería: cuantizar. Se eligen unos pocos umbrales, se traduce la medida a una categoría y se decide sobre la categoría. Lo interesante no es la mecánica, que cabe en tres líneas, sino las dos disciplinas que impone y que casi nadie respeta: que la medida se toma sobre la ventana y no sobre la pantalla, y que la categoría resultante autoriza a decidir sobre disposición pero nunca sobre identidad de dispositivo. Confundir ambas cosas es el error que reintroduce, con nombres modernos, exactamente la rigidez que se pretendía eliminar.
- Justificar los umbrales de 600dp y 840dp y situar las tallas grande y extragrande incorporadas después.
- Distinguir con rigor entre métricas de pantalla y métricas de ventana, y explicar cuándo divergen.
- Obtener y observar la clase de tamaño en Compose de forma que reaccione a cada cambio de geometría.
- Decidir qué preguntas pertenecen a la clase de tamaño y cuáles exigen otro instrumento.
De dónde salen los umbrales
El catálogo original tiene tres tallas de ancho y su origen no es estético sino ergonómico. Por debajo de 600dp hay espacio para una sola columna de contenido, porque una segunda columna dejaría ambas por debajo del ancho mínimo legible y porque el pulgar de una mano alcanza toda la superficie. Entre 600dp y 840dp caben dos columnas, aunque la segunda queda estrecha y suele funcionar mejor como panel auxiliar que como igual; es también la franja donde la navegación deja de tocarse con el pulgar y conviene moverla al borde. A partir de 840dp hay sitio para dos columnas cómodas o tres apretadas, y el modo de interacción dominante deja de ser táctil de una mano.
Las tallas de alto siguen la misma lógica con umbrales distintos: por debajo de 480dp la altura es tan escasa que cualquier contenido vertical extenso resulta hostil, hasta 900dp es la altura ordinaria de un teléfono en vertical, y por encima hay margen para estructuras apiladas. La talla de alto se ignora con demasiada frecuencia y es la que gobierna dos casos muy reales: el teléfono en horizontal, donde el ancho es expandido pero la altura es asfixiante, y la ventana de multiventana en la mitad inferior de la pantalla.
Ese primer caso merece detenerse porque es contraintuitivo y produce defectos vistosos. Un teléfono girado ofrece un ancho generoso y una altura mínima, y una interfaz que solo consulte el ancho concluirá que hay sitio para dos paneles justo cuando no hay sitio ni para uno vertical. La lectura correcta cruza ambos ejes: el ancho decide cuántas columnas, el alto decide cuánto contenido vertical cabe en cada una y si la navegación puede permitirse ocupar una franja horizontal. Ignorar el alto es la causa de que tantas aplicaciones muestren, en horizontal, un formulario donde solo se ve un campo y medio.
Conviene subrayar por qué los umbrales se expresan en unidades independientes de densidad y no en píxeles. Lo que limita el número de columnas no es la cantidad de puntos de imagen sino el tamaño físico aparente del contenido: una línea legible ocupa aproximadamente la misma superficie retiniana en cualquier pantalla, y las unidades independientes de densidad son precisamente la abstracción que normaliza esa superficie. Razonar en píxeles reintroduce la dependencia del panel concreto y produce umbrales que solo funcionan en el dispositivo donde se calibraron.
Con la llegada de las ventanas libres en escritorio, tres tallas se quedaron cortas por arriba: agrupar en la misma categoría una tablet de diez pulgadas y un monitor de veintisiete borra distinciones que sí importan. Por eso el catálogo se amplió con dos umbrales adicionales, que sitúan la talla grande a partir de 1200dp y la extragrande a partir de 1600dp. La consecuencia de diseño de esa ampliación es la aparición de un problema nuevo: en anchos enormes, la pregunta ya no es cuántos paneles caben sino cuánto ancho debe dejarse sin usar, porque estirar el contenido hasta el borde produce líneas ilegibles.
Ese problema tiene tres respuestas legítimas y conviene elegir conscientemente entre ellas, porque no son intercambiables. La primera es limitar el ancho del contenido y centrarlo, dejando márgenes generosos; funciona en lectura y en formularios, y es la opción por defecto cuando se duda. La segunda es aumentar el número de paneles o de columnas, que solo tiene sentido si existe contenido secundario con valor real y no relleno inventado para ocupar el hueco. Y la tercera es aumentar el aire —separaciones, alturas de línea, tamaños de imagen— sin tocar la estructura, que es la respuesta correcta cuando el contenido es visual. Lo que nunca es una respuesta es estirar los mismos elementos hasta el borde, aunque sea lo que el código hace por omisión.
Un umbral responde a la pregunta de qué estructura cabe, no a la de qué aparato hay. Una tablet de diez pulgadas en multiventana a la mitad está en la talla media; un teléfono grande en horizontal puede alcanzar la expandida. Ambas cosas son correctas y deseables: la estructura debe seguir al espacio, no al número de serie.
Ventana frente a pantalla
Esta es la distinción de la que depende todo lo demás, y la que más código heredado invalida. La pantalla es el panel físico y sus métricas son estables durante toda la sesión. La ventana es el rectángulo que el gestor de ventanas concede a tu actividad en este instante, y puede ser una fracción cualquiera de la pantalla. Divergen en multiventana, en pantalla dividida, en ventana flotante, en escritorio con ventanas libres y en cualquier configuración donde tu aplicación no sea la única en el panel.
De ahí sale la regla operativa: toda medida que gobierne decisiones de interfaz debe salir de las métricas de ventana. Las llamadas antiguas que devuelven el tamaño de la pantalla física siguen existiendo y siguen devolviendo un número; simplemente responden a otra pregunta.
flowchart LR A[Pantalla fisica] --> B[Gestor de ventanas] B --> C[Ventana concedida a tu actividad] C --> D[Metricas de ventana] D --> E[Clase de ancho] D --> F[Clase de alto] E --> G[Decision de estructura] F --> G A -.-> H[Metricas de pantalla] H -.-> I[Responden a otra pregunta] style D fill:#a6e3a1,color:#11111b style H fill:#f38ba8,color:#11111b
Hay un matiz que conviene fijar porque genera confusión recurrente: las métricas de ventana existen en dos variantes, la actual y la máxima. La actual describe el rectángulo de ahora mismo y es la que gobierna la interfaz. La máxima describe el mayor rectángulo que el sistema podría concederte en esta pantalla y sirve para decisiones de otro orden, como precargar recursos o dimensionar cachés. Usar la máxima para decidir estructura reproduce exactamente el error que se pretendía evitar, porque devuelve al razonamiento sobre el panel físico por la puerta de atrás.
// Fuera de la composicion, la fuente correcta es el calculador de metricas de ventana
val metricas = WindowMetricsCalculator.getOrCreate()
.computeCurrentWindowMetrics(actividad) // el rectangulo de ahora mismo
val anchoDp = metricas.bounds.width() / recursos.displayMetrics.density
Un segundo matiz, más sutil, afecta a los recortes del sistema. El rectángulo que recibes incluye las áreas ocupadas por las barras del sistema y por los recortes de cámara, de modo que el espacio realmente utilizable puede ser menor que el medido. Para decidir la clase de tamaño eso es irrelevante —los umbrales están calibrados sobre la ventana completa—, pero para colocar contenido no lo es en absoluto, y ahí manda el sistema de márgenes de seguridad que se trató al hablar de bordes.
Obtenerla y observarla en Compose
En Compose la clase de tamaño se obtiene de una función de composición que devuelve la información adaptativa actual de la ventana. Al ser una lectura de estado dentro de la composición, cualquier cambio de geometría provoca recomposición automáticamente, que es justo el comportamiento que se necesita en un escritorio con ventanas libres donde la anchura cambia mientras el usuario arrastra.
@Composable
fun RaizDeLaApp(contenido: @Composable (WindowSizeClass) -> Unit) {
val info = currentWindowAdaptiveInfo()
val clase = info.windowSizeClass
contenido(clase)
}
La forma de consultarla evolucionó de manera importante. La API original exponía enumeraciones comparables entre sí y el código las trataba con una expresión when sobre tres constantes. La API vigente prefiere preguntas del tipo el ancho alcanza al menos este punto de corte, expresadas con funciones de umbral. El cambio no es cosmético: una comparación por igualdad contra una enumeración cerrada se rompe silenciosamente cuando el catálogo crece —y creció—, mientras que una pregunta por umbral sigue siendo correcta cuando aparecen tallas nuevas por encima.
val clase = currentWindowAdaptiveInfo().windowSizeClass
when {
clase.isWidthAtLeastBreakpoint(WIDTH_DP_EXPANDED_LOWER_BOUND) -> TresPaneles()
clase.isWidthAtLeastBreakpoint(WIDTH_DP_MEDIUM_LOWER_BOUND) -> ListaDetalle()
else -> ColumnaUnica()
}
Un apunte sobre la reacción al cambio que conviene no dar por supuesto. Que la clase se lea dentro de la composición no garantiza por sí solo que la interfaz responda con fluidez a un arrastre continuo del borde de la ventana: garantiza que se recomponga con el valor correcto. Si la disposición cambia de golpe entre dos tallas sin transición, el usuario percibe un salto brusco en mitad de su gesto. La corrección es animar el cambio estructural, y de nuevo las estructuras adaptativas de la biblioteca lo traen resuelto mientras que una bifurcación escrita a mano no.
Fíjate en el orden descendente de las comprobaciones y en la rama final sin condición. Esa estructura tiene una propiedad valiosa: es exhaustiva por construcción y sigue siendo correcta si mañana aparece un umbral mayor, porque cualquier talla nueva entra por la rama más alta que satisfaga. Escribir las comprobaciones en orden ascendente o cerrar con una igualdad produce código que envejece mal.
Merece la pena señalar que el objeto devuelto por esa función de composición no contiene solo la clase de tamaño. Transporta también la información de postura del dispositivo, que es la materia de la lección sobre plegables, y por eso conviene tratarlo como el punto único de entrada de todo lo adaptativo en lugar de como un accesorio para consultar anchuras. Los scaffolds adaptativos de la biblioteca lo consumen entero, y esa es la razón de que sepan tratar una bisagra sin que nadie se lo pida.
Queda una decisión de arquitectura que importa más que la sintaxis: dónde se calcula la clase. La respuesta es una sola vez, cerca de la raíz, y desde ahí desciende como un parámetro ordinario hasta los pocos composables que deciden estructura. Calcularla dentro de cada componente reintroduce el acoplamiento que se quería eliminar y hace que cada pieza sea imposible de previsualizar y de probar sin un entorno completo. Un componente que recibe un parámetro de estructura se prueba con un valor literal; uno que consulta el entorno necesita un dispositivo.
La clase de tamaño describe la ventana entera, no el hueco donde vive un componente concreto. Una tarjeta situada dentro de un panel lateral de 320dp está en una ventana expandida y en un espacio local compacto a la vez. Decidir la disposición interna de esa tarjeta mirando la clase de ventana produce contenido desbordado. Para el espacio local, la herramienta correcta es la que expone los Constraints recibidos o un contenedor propio, y la clase de ventana debe quedarse en las decisiones de estructura de pantalla.
Qué preguntas pertenecen a esta abstracción
Conviene trazar la frontera con precisión, porque el mal uso más común de las clases de tamaño no es técnico sino categorial: pedirles que respondan preguntas que no son suyas.
Decisiones de estructura
Cuántos paneles hay, dónde vive la navegación, si el detalle se abre encima o al lado, si la acción principal es un botón flotante o un botón en una barra. Todas son suyas.
Identidad de dispositivo
Si es tablet, si es plegable, si hay ratón, si hay teclado, si la pantalla es táctil. Ninguna se deduce del tamaño y todas tienen su propia fuente de verdad.
Capacidades de entrada
El modo de interacción se consulta al sistema de entrada, no al tamaño. Hay pantallas expandidas puramente táctiles y ventanas compactas manejadas con teclado.
Ajuste fino de contenido
Cuántas columnas caben en una rejilla o si un texto necesita dos líneas son preguntas de espacio local. Se responden midiendo el hueco, no la ventana.
La confusión más extendida de todas es la que iguala talla expandida con presencia de teclado y ratón. Son ejes independientes y su independencia es fácil de comprobar: una tablet de doce pulgadas sin accesorios es expandida y puramente táctil, mientras que un teléfono conectado a un monitor con teclado es una ventana que puede ser compacta y manejarse íntegramente con el cursor. Diseñar densidades de toque a partir de la talla produce objetivos táctiles demasiado pequeños en tablets grandes; diseñar atajos de teclado a partir de la talla los deja inaccesibles justo donde más falta hacen.
El caso que mejor ilustra la frontera es el de la funcionalidad. Una tentación frecuente consiste en usar la clase de tamaño para habilitar o deshabilitar prestaciones: la edición avanzada solo en expandido, el panel de filtros solo en medio. Es un error de producto disfrazado de decisión técnica. La talla de ventana describe cuánto espacio hay para presentar algo, no si el usuario tiene derecho a ello. Un usuario que pliega su dispositivo no pierde la necesidad de editar; pierde sitio para mostrar la edición al lado, lo que implica presentarla de otra forma —a pantalla completa, en una hoja, en un paso posterior— pero jamás retirarla. La regla, enunciada de forma comprobable, es que el conjunto de acciones disponibles debe ser invariante respecto a la geometría de la ventana; lo único que puede variar es su presentación. Formulada así, deja de ser una recomendación de estilo y se convierte en algo que una prueba automatizada puede afirmar, y en la última lección se verá exactamente cómo.
Queda un último apunte sobre el uso de la talla en el resto de la arquitectura. La clase de tamaño es una propiedad del entorno de presentación y no pertenece al modelo de dominio ni a la capa de datos: un repositorio no debe saber si la ventana es ancha, y una petición de red no debe cambiar de forma porque el usuario haya arrastrado un borde. Cuando aparece la tentación de propagarla hacia abajo —para pedir menos elementos en compacto, por ejemplo—, casi siempre la solución correcta es que la capa de presentación pida lo que necesita y la de datos siga siendo indiferente a la geometría.
Merece la pena detenerse en lo que ocurre realmente cuando se traduce un ancho de 743 puntos a la etiqueta media, porque es una operación con una estructura profunda que reaparece por todas partes en ingeniería y cuya lógica, una vez vista, cambia cómo se diseñan abstracciones en general. Lo que se hace es destruir información deliberadamente. El número exacto contenía más datos que la categoría, y sin embargo la categoría es más útil, porque el valor de una abstracción no reside en cuánto conserva sino en cuánto permite razonar y verificar. Con tres o cinco tallas, el espacio de estados de la interfaz es finito, enumerable y comprobable: se pueden dibujar todas las variantes, revisarlas con diseño, probarlas automáticamente y discutirlas en una reunión. Con un continuo, no existe conjunto de pruebas que agote los casos y ninguna afirmación sobre el comportamiento es verificable. La cuantización compra decidibilidad al precio de exactitud, y en el diseño de interfaces esa transacción sale casi siempre a favor. Ahora bien, toda cuantización tiene un coste que hay que saber dónde aparece, y aquí aparece en dos lugares muy concretos. El primero son las fronteras: dos anchos que difieren en un punto pueden producir disposiciones radicalmente distintas, y si el usuario está arrastrando el borde de una ventana verá ese salto en directo. Por eso las transiciones entre tallas deben animarse y por eso los umbrales deben elegirse donde el salto tenga sentido perceptivo y no donde caiga una cifra redonda. El segundo es la tentación de sobreajustar: cada vez que alguien propone una talla intermedia para resolver un caso incómodo, está proponiendo devolver información al sistema y perder decidibilidad, y el balance rara vez compensa. La disciplina consiste en resistir esa tentación en el eje del tamaño y, cuando el caso incómodo persista, reconocer que probablemente no era un problema de talla sino de espacio local, que tiene su propio instrumento. Quien interioriza esto deja de ver las clases de tamaño como una tabla de umbrales que hay que memorizar y empieza a verlas como lo que son: la decisión de reducir un problema infinito a uno finito para poder hablar de él con otras personas.
- Calcula la clase de tamaño una sola vez en la raíz de tu aplicación y hazla descender como parámetro. Cuenta cuántos componentes dejan de consultar el entorno al hacerlo.
- Sustituye cualquier comparación por igualdad contra una enumeración de tallas por comprobaciones de umbral en orden descendente. Explica por qué la nueva versión sobrevive a un catálogo ampliado.
- Localiza todo uso de métricas de pantalla o de métricas máximas de ventana en decisiones de interfaz y sustitúyelo por métricas actuales. Prueba el resultado en pantalla dividida.
- Añade la clase de alto a una pantalla que hoy solo mire el ancho y comprueba qué ocurre con tu teléfono en horizontal.
- Busca una prestación que hoy solo exista en tallas grandes y rediseña su presentación compacta sin retirarla. Documenta la decisión.