Cómo brindar compatibilidad con diferentes densidades de píxeles

Los dispositivos Android no solo tienen diferentes tamaños de pantalla, como teléfonos celulares, tablets, TVs, etc., pero también tienen pantallas con diferentes tamaños de píxeles. Uno dispositivo puede tener 160 píxeles por pulgada, mientras que otro dispositivo puede tener 160 píxeles por pulgada, píxeles en el mismo espacio. Si no consideras estas variaciones en la densidad de píxeles, el sistema podría escalar sus imágenes, lo que hará que se vean borrosas, o las imágenes podrían aparecen con el tamaño incorrecto.

En esta página, se muestra cómo puedes diseñar tu app para admitir diferentes densidades de píxeles mediante unidades de medida independientes de la resolución y proporciona recursos alternativos de mapas de bits para cada densidad de píxeles.

Mira el siguiente video para obtener una descripción general de estas técnicas.

Para obtener más información sobre el diseño de elementos de íconos, consulta los lineamientos de íconos de Material Design.

Cómo utilizar píxeles independientes de la densidad

Evita usar píxeles para definir distancias o tamaños. Define las dimensiones con píxeles es un problema, ya que las diferentes pantallas tienen distintas densidades de píxeles por lo que la misma cantidad de píxeles corresponde a distintos tamaños físicos en diferentes dispositivos.

Una imagen que muestra dos ejemplos de pantallas de dispositivos con diferentes densidades
Figura 1: Dos pantallas del mismo tamaño pueden tener una cantidad diferente de píxeles.

Para conservar el tamaño visible de la IU en pantallas con diferentes densidades, diseña tu IU usando píxeles independientes de la densidad (dp) como unidad de medida Un dp equivale a Unidad de píxeles virtual que es aproximadamente igual a un píxel en una pantalla de densidad media. (160 dpi o la densidad del “modelo de referencia”). Android traduce este valor al la cantidad adecuada de píxeles reales para cada densidad.

Considera los dos dispositivos de la figura 1. Una vista que es 100 píxeles de ancho aparecen mucho más grandes en el dispositivo de la izquierda. Una vista definido como 100 dp de ancho aparece del mismo tamaño en ambas pantallas.

Cuando defines tamaños de texto, puedes usar píxeles (sp) como unidades. La unidad de sp es tiene el mismo tamaño que un dp de forma predeterminada, pero se modifica según la configuración tamaño del texto. Nunca uses sp para los tamaños del diseño.

Por ejemplo, para especificar el espaciado entre dos vistas, usa dp:

<Button android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/clickme"
    android:layout_marginTop="20dp" />

Para especificar el tamaño del texto, usa sp:

<TextView android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="20sp" />

Cómo convertir unidades de dp a unidades de píxeles

En algunos casos, debes expresar las dimensiones en dp y, luego, convertirlos en píxeles. La conversión de unidades dp a píxeles de pantalla es la siguiente:

px = dp * (dpi / 160)

Nota: Nunca codifiques esta ecuación para calcular píxeles. En cambio, usa TypedValue.applyDimension(), que convierte muchos tipos de dimensiones (dp, sp, etc.) en píxeles por ti.

Imagina una app en la que se reconoce un gesto de desplazamiento o lanzamiento después de que el dedo del usuario se haya movido al menos 16 píxeles. En un modelo de referencia pantalla, el dedo del usuario debe mover 16 pixels / 160 dpi, que equivale a 1/10 de pulgada (o 2.5 mm), antes se reconoce el gesto.

En un dispositivo con una pantalla de alta densidad (240 dpi), el dedo del usuario se debe mover 16 pixels / 240 dpi, que equivale a 1/15 de pulgada (o 1.7 mm). La distancia es mucho más corta y por lo tanto, la aplicación parece más sensible para el usuario.

Para solucionar este problema, expresa el umbral del gesto en el código en dp y para convertirlos en píxeles reales. Por ejemplo:

Kotlin

// The gesture threshold expressed in dp
private const val GESTURE_THRESHOLD_DP = 16.0f

private var gestureThreshold: Int = 0

// Convert the dps to pixels, based on density scale
gestureThreshold = TypedValue.applyDimension(
  COMPLEX_UNIT_DIP,
  GESTURE_THRESHOLD_DP + 0.5f,
  resources.displayMetrics).toInt()

// Use gestureThreshold as a distance in pixels...

Java

// The gesture threshold expressed in dp
private final float GESTURE_THRESHOLD_DP = 16.0f;

// Convert the dps to pixels, based on density scale
int gestureThreshold = (int) TypedValue.applyDimension(
  COMPLEX_UNIT_DIP,
  GESTURE_THRESHOLD_DP + 0.5f,
  getResources().getDisplayMetrics());

// Use gestureThreshold as a distance in pixels...

El campo DisplayMetrics.density especifica el factor de escala usado para convertir unidades dp a de acuerdo con la densidad de píxeles actual. En una pantalla de densidad media, DisplayMetrics.density es igual a 1.0 y, en una pantalla de alta densidad, es igual a 1.5. En una pantalla de densidad extraalta, equivale a 2.0 y, en una pantalla de baja densidad, a 0.75. Esta cifra es usada por TypedValue.applyDimension() para obtener el recuento real de píxeles para la pantalla actual.

Cómo usar valores de configuración escalados previamente

Puedes usar la clase ViewConfiguration para acceder a las distancias, las velocidades y los tiempos que usa el sistema Android. Por ejemplo, el distancia en píxeles que usa el framework como se puede obtener el umbral de desplazamiento con getScaledTouchSlop():

Kotlin

private val GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).scaledTouchSlop

Java

private final int GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).getScaledTouchSlop();

Métodos en ViewConfiguration que comienzan con el prefijo getScaled muestran un valor en píxeles que se visualizan correctamente, independientemente de la densidad de píxeles.

Preferir gráficos vectoriales

Una alternativa a la creación de varias versiones específicas de densidad de una imagen es crear solo un gráfico vectorial. Los gráficos vectoriales crean una imagen con XML para definen rutas y colores, en lugar de usar mapas de bits de píxeles. Como tal, el vector Los gráficos pueden escalar a cualquier tamaño sin artefactos de escalamiento, aunque, por lo general, ideal para ilustraciones como iconos, no fotografías.

Los gráficos vectoriales a menudo se proporcionan como archivos SVG (gráficos vectoriales escalables), pero Android no admite este formato, así que debes convertir los archivos SVG a Vector de Android drawable.

Puedes convertir un SVG a un elemento de diseño vectorial usando la biblioteca Vector Asset Studio de la siguiente manera:

  1. En la ventana Project, haz clic con el botón derecho en el directorio res y selecciona Nuevo > Elemento vectorial.
  2. Selecciona Archivo local (SVG, PSD).
  3. Ubica el archivo que quieras importar y realiza las modificaciones necesarias.

    Una imagen que muestra cómo importar SVG en Android Studio
    Figura 2: Importación de un SVG con Android Studio

    Es posible que notes algunos errores en la ventana de Asset Studio , que indica que los elementos de diseño vectoriales no admiten algunas propiedades del archivo. Esto no impide que importes el archivo. las propiedades no admitidas se ignoran.

  4. Presiona Siguiente.

  5. En la siguiente pantalla, confirma el conjunto de orígenes donde quieras colocar el archivo en tu proyecto. y haz clic en Finalizar.

    Como un elemento de diseño vectorial puede usarse en todas las densidades de píxeles, este archivo va en tu directorio de elementos de diseño predeterminados, como se muestra en el siguiente en la nube. No es necesario que uses directorios específicos de densidad.

    res/
      drawable/
        ic_android_launcher.xml
    

Para obtener más información sobre cómo crear gráficos vectoriales, consulta el artículo sobre elementos de diseño vectorial. en la documentación de Google Cloud.

Cómo proporcionar mapas de bits alternativos

Para proporcionar una buena calidad gráfica en dispositivos con diferentes densidades de píxeles, proporcionan varias versiones de cada mapa de bits en tu app, una para cada de densidad, con la resolución correspondiente. De lo contrario, Android debe escalar tu mapa de bits para que ocupe el mismo espacio visible en cada pantalla, lo que da como resultado artefactos de escalamiento, como el desenfoque.

Una imagen que muestra tamaños relativos para mapas de bits en diferentes tamaños de densidad
Figura 3: Tamaños relativos de mapas de bits en diferentes buckets de densidad.

Hay muchos intervalos de densidad disponibles para usar en tus apps. Tabla 1 describe los diferentes calificadores de configuración disponibles y los tipos de pantalla a los que se aplican.

Tabla 1: Calificadores de configuración para diferentes densidades de píxeles.

Calificador de densidad Descripción
ldpi Recursos para pantallas de densidad baja (ldpi) (~120 dpi)
mdpi Recursos para pantallas de densidad media (mdpi) (~160 dpi) Este es el modelo de referencia densidad.
hdpi Recursos para pantallas de densidad alta (hdpi) (~240 dpi)
xhdpi Recursos para pantallas de densidad muy alta (xhdpi) (~320 dpi)
xxhdpi Recursos para pantallas de densidad muy, muy alta (xxhdpi) (~480 dpi)
xxxhdpi Recursos para usos de densidad extremadamente alta (xxxhdpi) (~640 dpi)
nodpi Recursos para todas las densidades. Estos son recursos independientes de la densidad. El sistema no escala recursos etiquetados con este calificador, independientemente de la densidad de la pantalla actual.
tvdpi Recursos para pantallas entre mdpi y hdpi; aproximadamente ~213 dpi. Este no se considera un grupo de densidad "principal". Se prevé principalmente para televisiones, y la mayoría de las apps no lo necesitan, ya que proporcionan mdpi y hdpi recursos es suficiente para la mayoría de las apps y el sistema las escala según lo que sea apropiado. Si consideras necesario proporcionar recursos de tvdpi, y dimensionarlas con un factor de 1.33 * mdpi. Por ejemplo, una imagen de 100 x 100 píxeles para Las pantallas mdpi son de 133 x 133 píxeles para tvdpi.

Para crear elementos de diseño alternativos de mapa de bits para diferentes densidades, sigue el Proporción de escalamiento de 3:4:6:8:12:16 entre las seis densidades principales. Por ejemplo, si tienes un elemento de diseño de mapa de bits de 48 x 48 píxeles para pantallas de densidad media, los tamaños son:

  • 36 x 36 (0.75x) para densidad baja (ldpi)
  • 48 x 48 (referencia de 1.0x) para densidad media (mdpi)
  • 72 × 72 (1.5x) para densidad alta (hdpi)
  • 96 x 96 (2.0x) para densidad muy alta (xhdpi)
  • 144 x 144 (3.0x) para densidad muy muy alta (xxhdpi)
  • 192 x 192 (4.0x) para densidad extremadamente alta (xxxhdpi)

Coloca los archivos de imagen generados en el subdirectorio correspondiente. por debajo de res/:

res/
  drawable-xxxhdpi/
    awesome_image.png
  drawable-xxhdpi/
    awesome_image.png
  drawable-xhdpi/
    awesome_image.png
  drawable-hdpi/
    awesome_image.png
  drawable-mdpi/
    awesome_image.png

Entonces, cada vez que hagas referencia a @drawable/awesomeimage, el sistema selecciona el mapa de bits apropiado en función de los DPI de la pantalla. Si no proporcionas un recurso específico de densidad para esa densidad, el sistema localiza la siguiente mejor opción y la escala para que se adapte a la pantalla.

Sugerencia: Si tienes recursos de elementos de diseño que no desea que el sistema escale, como cuando realiza algunos ajustes a la imagen durante el tiempo de ejecución, colócalos en un con el calificador de configuración nodpi. Los recursos con este calificador se consideran independientes de la densidad. el sistema no las escala.

Para obtener más información sobre otros calificadores de configuración y cómo Android selecciona los recursos adecuados para la configuración actual de la pantalla, consulta la Descripción general de los recursos de la app.

Cómo agregar íconos de apps en los directorios mipmap

Al igual que con otros elementos de mapas de bits, debes proporcionar versiones específicas de densidad de el ícono de la app. Sin embargo, algunos selectores de aplicaciones muestran el ícono de la app hasta en un 25%. mayor que la requerida por el bucket de densidad del dispositivo.

Por ejemplo, si el intervalo de densidad de un dispositivo es xxhdpi y el ícono de app más grande proporcionar está en drawable-xxhdpi, el selector de aplicaciones escala este ícono, lo que hace que parezca menos nítido.

Para evitar esto, coloca Los íconos de tu app están en los directorios mipmap en lugar de en drawable. Desmarcar “Me gusta” Los directorios drawable, los directorios mipmap se retienen en el APK, incluso si compilas APK específicos de densidad. Esto permite que las apps de selector elijan la mejor de resolución de Google para que aparezca en la pantalla de inicio.

res/
  mipmap-xxxhdpi/
    launcher_icon.png
  mipmap-xxhdpi/
    launcher_icon.png
  mipmap-xhdpi/
    launcher_icon.png
  mipmap-hdpi/
    launcher_icon.png
  mipmap-mdpi/
    launcher_icon.png

En el ejemplo anterior de un dispositivo xxhdpi, puedes proporcionar una ícono de selector de mayor densidad en el directorio mipmap-xxxhdpi.

Para obtener los lineamientos de diseño de íconos, consulta Íconos del sistema.

Para obtener ayuda sobre cómo crear íconos de apps, consulta Cómo crear íconos de apps con Image Asset Studio.

Sugerencias para problemas de densidad poco comunes

En esta sección, se describe cómo Android realiza el escalamiento para mapas de bits. con distintas densidades de píxeles y cómo se puede controlar los mapas de bits se dibujan en diferentes densidades. A menos que tu app manipule gráficos o tienes problemas al ejecutar la app con distintas densidades de píxeles, puedes ignorar esta sección.

Para entender mejor cómo puedes admitir varias densidades al manipular gráficos en de ejecución, debes saber cómo ayuda el sistema a garantizar la escala adecuada para los mapas de bits. Esto se hace de las siguientes maneras:

  1. Ajuste previo de los recursos, como los elementos de diseño del mapa de bits

    Según la densidad de la pantalla actual, el sistema usa cualquier densidad recursos de tu app. Si los recursos no están disponibles en la densidad correcta, el sistema carga los recursos predeterminados y los aumenta o reduce según sea necesario. El sistema supone que los recursos predeterminados (los de un sin calificadores de configuración) están diseñadas para el modelo de referencia de píxeles (mdpi) y cambia el tamaño de esos mapas de bits al tamaño adecuado para la densidad de píxeles actual.

    Si solicitas las dimensiones de un recurso escalado previamente, el sistema muestra valores que representan las dimensiones después del escalamiento. Por ejemplo, un mapa de bits diseñado en 50 x 50 píxeles para una pantalla mdpi se ajusta a 75 x 75 píxeles en una pantalla hdpi (si no hay un recurso alternativo para hdpi), y el sistema informa el tamaño como tal.

    Hay algunas situaciones en las que tal vez no te convenga que Android escale previamente. un recurso. La manera más sencilla de evitar el ajuste previo es colocar el recurso en un directorio con el calificador de configuración nodpi Por ejemplo:

    res/drawable-nodpi/icon.png

    Cuando el sistema usa el mapa de bits icon.png de esta carpeta, no lo escala. según la densidad del dispositivo actual.

  2. Ajuste de escala automático de dimensiones y coordenadas de píxeles

    Para inhabilitar las dimensiones y las imágenes con escalamiento previo, configura android:anyDensity a "false" en el manifiesto o de manera programática para un Bitmap si estableces inScaled en "false". En en este caso, el sistema escala automáticamente cualquier coordenada absoluta de píxeles y valores de dimensión al momento de la extracción. Esto garantiza que los píxeles definidos los elementos de pantalla siguen mostrándose casi en el mismo tamaño físico. que se pueden mostrar en la densidad de píxeles de referencia (mdpi). El sistema se encarga este escalamiento se aplica de manera transparente a la app e informa el píxel ajustado dimensiones de la app, en lugar de dimensiones físicas de píxeles.

    Por ejemplo, supongamos que un dispositivo tiene una pantalla de alta densidad WVGA, que tiene una resolución de 480 x 800 y aproximadamente el del mismo tamaño que una pantalla HVGA tradicional, pero ejecuta una aplicación que ha inhabilitado sin escalar previamente. En este caso, el sistema “mienta” a la app cuando solicita la visualización las dimensiones y los informes de 320 x 533, la traducción en mdpi aproximada para la densidad de píxeles.

    Luego, cuando la aplicación realiza operaciones de dibujo, como invalidar un rectángulo de (10,10) a (100, 100), el sistema transforma las coordenadas ajustándolas la cantidad adecuada y, de hecho, invalida la región (15,15) a (150, 150). Esta discrepancia puede causar un comportamiento inesperado si Tu app manipula directamente el mapa de bits ajustado, pero esto se considera un para garantizar el mejor rendimiento posible de la app. Si encuentras este consulta Cómo convertir unidades dp a píxel unidades.

    Por lo general, no se inhabilita el escalamiento previo. La mejor manera de admitir varias pantallas es seguir las técnicas básicas descritas en esta página.

Si tu app manipula mapas de bits o interactúa directamente con píxeles en la pantalla. de alguna otra manera, podrías necesitar pasos adicionales para admitir diferentes densidades de píxeles. Por ejemplo, si respondes a gestos táctiles contando los la cantidad de píxeles que atraviesa un dedo, debes usar el de píxeles independientes de la densidad, en lugar de píxeles reales, pero puedes convertir valores de dp a px.

Cómo probar la app en todas las densidades de píxeles

Cómo probar tu app en varios dispositivos con diferentes píxeles para garantizar que la IU se escale correctamente. Prueba en un dispositivo físico el dispositivo cuando sea posible; usa el botón Android emulador si no tienes acceso a para las distintas densidades de píxeles.

Si quieres realizar pruebas en dispositivos físicos, pero no quieres comprar los dispositivos, puedes usar Firebase Test Lab para acceder a los dispositivos de un centro de datos de Google.