Cómo brindar compatibilidad con diferentes tamaños de pantalla

La compatibilidad con diferentes tamaños de pantalla permite que una mayor variedad de dispositivos y usuarios accedan a tu app.

Para admitir tantos tamaños de pantalla como sea posible, los diseños de tu app deben ser responsivos y adaptables. Los diseños responsivos y adaptables proporcionan una experiencia del usuario optimizada sin importar el tamaño de la pantalla, lo que permite que tu app se adapte a teléfonos, tablets, plegables, dispositivos ChromeOS, orientaciones vertical y horizontal, y a parámetros de configuración que cambien de tamaño, como el modo multiventana.

Clases de tamaño de 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 responsivos o adaptables. Los puntos de interrupción equilibran la simplicidad del diseño con la flexibilidad para optimizar tu app en casos únicos.

Las clases de tamaño de ventana categorizan el área de visualización disponible para tu app como compacta, mediana o expandida. El ancho y la altura disponibles se clasifican por separado, por lo que en cualquier momento, tu app tiene dos clases de tamaño de ventana: una para el ancho y otra para la altura. Por lo general, el ancho disponible es más importante que la altura disponible debido a la ubicuidad del desplazamiento vertical, por lo que la clase de tamaño de ancho de ventana probablemente sea más relevante para la IU de tu app.

Figura 1: Representaciones de clases de tamaños de ventana basadas en el ancho
Figura 2: Representaciones de clases de tamaños de ventana basadas en el alto

Como se muestra en las figuras, los puntos de interrupción te permiten seguir pensando en los diseños en términos de dispositivos y configuraciones. Cada punto de interrupción de clase de tamaño representa la mayoría de los casos en cada dispositivo típico, lo que puede ser un marco de referencia útil cuando piensas en la forma de tus diseños basados en puntos de interrupción.

Clase de tamaño Punto de interrupción Representación del dispositivo
Ancho compacto ancho <600 dp 99.96% de los teléfonos en orientación vertical
Ancho medio 600 dp ≤ ancho < 840 dp 93.73% de las tablets en orientación vertical

la mayoría de las pantallas internas desplegadas en orientación vertical

Ancho expandido ancho ≥840 dp 97.22% de las tablets en orientación horizontal

la mayoría de las pantallas internas desplegadas en orientación horizontal

Altura compacta altura <480 dp 99.78% de los teléfonos en orientación horizontal
Altura media 480 dp ≤ altura <900 dp 96.56% de las tablets en orientación horizontal

97.59% de los teléfonos en orientación vertical

Altura expandida altura ≥900 dp 94.25% de las tablets en orientación vertical

Si bien visualizar las clases de tamaño como dispositivos físicos puede ser útil, las clases de tamaño de ventana no están determinadas explícitamente por el tamaño de la pantalla del dispositivo. Las clases de tamaño de ventana no están diseñadas para la lógica isTablet‐type. En su lugar, las clases de tamaño de ventana están determinadas por el tamaño de ventana disponible para tu aplicación, independientemente del tipo de dispositivo en el que se ejecute la app, lo que tiene dos implicaciones importantes:

  • Los dispositivos físicos no garantizan una clase específica de tamaño de ventana. El espacio de pantalla disponible para tu app puede diferir del tamaño de la pantalla del dispositivo por varios motivos. En los dispositivos móviles, el modo de pantalla dividida puede particionar la pantalla entre dos aplicaciones. En ChromeOS, las apps para Android se pueden presentar en ventanas de formato libre cuyo tamaño puede cambiar de forma arbitraria. Los dispositivos plegables pueden tener dos pantallas de diferentes tamaños a las que se puede acceder de forma individual al plegar o desplegar el dispositivo.

  • La clase de tamaño de ventana puede cambiar durante el ciclo de vida de la app. Mientras se ejecuta la app, los cambios de orientación, la realización de varias tareas y el plegado y desplegado del dispositivo pueden impactar en el espacio de pantalla disponible. Como resultado, la clase de tamaño de ventana es dinámica y la IU de tu app debería adaptarse en consecuencia.

Las clases de tamaño de ventana se asignan a los puntos de interrupción de diseño en la cuadrícula de diseño responsivo de Material Design. Usa clases de tamaño de ventana para tomar decisiones de diseño de aplicaciones de alto nivel, como decidir si usar un diseño canónico específico para aprovechar el espacio de pantalla adicional.

Puedes calcular la WindowSizeClass actual con la función WindowSizeClass#compute() que proporciona la biblioteca Jetpack WindowManager. A continuación, se muestra un ejemplo de cómo calcular la clase de tamaño de ventana y recibir actualizaciones cada vez que esta cambie:

Kotlin

class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // ...

        // Replace with a known container that you can safely add a
        // view to where the view won't affect the layout and the view
        // won't be replaced.
        val container: ViewGroup = binding.container

        // Add a utility view to the container to hook into
        // View.onConfigurationChanged(). This is required for all
        // activities, even those that don't handle configuration
        // changes. You can't use Activity.onConfigurationChanged(),
        // since there are situations where that won't be called when
        // the configuration changes. View.onConfigurationChanged() is
        // called in those scenarios.
        container.addView(object : View(this) {
            override fun onConfigurationChanged(newConfig: Configuration?) {
                super.onConfigurationChanged(newConfig)
                computeWindowSizeClasses()
            }
        })

        computeWindowSizeClasses()
    }

    private fun computeWindowSizeClasses() {
        val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(this)
        val width = metrics.bounds.width()
        val height = metrics.bounds.height()
        val density = resources.displayMetrics.density
        val windowSizeClass = WindowSizeClass.compute(width/density, height/density)
        // COMPACT, MEDIUM, or EXPANDED
        val widthWindowSizeClass = windowSizeClass.windowWidthSizeClass
        // COMPACT, MEDIUM, or EXPANDED
        val heightWindowSizeClass = windowSizeClass.windowHeightSizeClass

        // Use widthWindowSizeClass and heightWindowSizeClass.
    }
}

Java

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // ...

        // Replace with a known container that you can safely add a
        // view to where the view won't affect the layout and the view
        // won't be replaced.
        ViewGroup container = binding.container;

        // Add a utility view to the container to hook into
        // View.onConfigurationChanged(). This is required for all
        // activities, even those that don't handle configuration
        // changes. You can't use Activity.onConfigurationChanged(),
        // since there are situations where that won't be called when
        // the configuration changes. View.onConfigurationChanged() is
        // called in those scenarios.
        container.addView(new View(this) {
            @Override
            protected void onConfigurationChanged(Configuration newConfig) {
                super.onConfigurationChanged(newConfig);
                computeWindowSizeClasses();
            }
        });

        computeWindowSizeClasses();
    }

    private void computeWindowSizeClasses() {
        WindowMetrics metrics = WindowMetricsCalculator.getOrCreate()
                .computeCurrentWindowMetrics(this);

        int width = metrics.getBounds().width
        int height = metrics.getBounds().height()
        float density = getResources().getDisplayMetrics().density;
        WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density)
        // COMPACT, MEDIUM, or EXPANDED
        WindowWidthSizeClass widthWindowSizeClass = windowSizeClass.getWindowWidthSizeClass()
        // COMPACT, MEDIUM, or EXPANDED
        WindowHeightSizeClass heightWindowSizeClass = windowSizeClass.getWindowHeightSizeClass()

        // Use widthWindowSizeClass and heightWindowSizeClass.
    }
}

Una vez que observes las clases de tamaño de ventana en tu app, estarás listo para comenzar a cambiar tu diseño en función de la clase de tamaño de ventana actual.

Diseños y clases de tamaño de ventana

A medida que realices cambios en el diseño, prueba su comportamiento en todos los tamaños de ventana, especialmente en los anchos de puntos de interrupción compacto, medio y expandido.

Si ya tienes un diseño para pantallas compactas, primero optimiza tu diseño para la clase de tamaño de ancho expandido, ya que esta clase de tamaño proporciona el mayor espacio para cambios adicionales de contenido y de IU. Luego, decide qué diseño es conveniente para la clase de tamaño mediano. Considera agregar un diseño especializado.

Próximos pasos

Si deseas obtener más información sobre cómo usar las clases de tamaño de ventana para crear diseños responsivos o adaptables, consulta lo siguiente:

Para obtener más información sobre los aspectos que hacen que una app sea excelente en todos los dispositivos y tamaños de pantalla, consulta los siguientes artículos: