Ilustración de Virginia Poltrack

Comprender el formato de imagen vectorial de Android: VectorDrawable

Los dispositivos Android vienen en todos los tamaños, formas y densidades de pantalla. Es por eso que soy un gran fanático del uso de activos vectoriales independientes de resolución. ¿Pero qué son exactamente? ¿Cuáles son sus beneficios? Cuales son los costos? ¿Cuándo debo usarlos? ¿Cómo los creas y usas? En esta serie de publicaciones, me gustaría explorar estas preguntas y explicar por qué creo que la gran mayoría de los activos en sus aplicaciones deben ser vectores y cómo aprovecharlos al máximo.

Raster vs Vector

La mayoría de los formatos de imagen (png, jpeg, bmp, gif, webp, etc.) son ráster, lo que significa que describen la imagen como una cuadrícula fija de píxeles. Como tales, se definen en una resolución particular y no entienden nada sobre su contenido, solo el color de cada píxel. Sin embargo, los gráficos vectoriales describen la imagen como una serie de formas definidas sobre un tamaño de lienzo abstracto.

¿Por qué vector?

Los activos vectoriales tienen 3 beneficios principales, que son:

  • Agudo
  • Pequeña
  • Dinámica

Agudo

Las imágenes vectoriales cambian de tamaño con gracia; porque describen la imagen sobre un tamaño de lienzo abstracto, puede escalar este lienzo hacia arriba o hacia abajo y luego volver a dibujar la imagen en ese tamaño. Sin embargo, los activos de trama pueden deteriorarse cuando los redimensiona. La reducción de los activos ráster tiende a estar bien (ya que está descartando información), pero la ampliación de ellos conduce a artefactos como borrosidad o bandas, ya que tienen que interpolar los píxeles faltantes.

Artefactos de (extremo) ampliando una imagen ráster (izquierda) frente a una imagen vectorial (derecha)

Es por eso que en Android necesitamos proporcionar múltiples versiones de cada activo ráster para diferentes pantallas de densidad:

  • res / drawable-mdpi / foo.png
  • res / drawable-hdpi / foo.png
  • res / drawable-xhdpi / foo.png
  • ...

Android elige la densidad más grande más cercana y la reduce (si es necesario). Con la tendencia de los dispositivos con pantallas cada vez más densas, los fabricantes de aplicaciones deben seguir creando, incluyendo y enviando versiones cada vez más grandes de los mismos activos. Tenga en cuenta que muchos dispositivos modernos no se ubican en grupos de densidad exactos (por ejemplo, el Pixel 3 XL es de 552 ppp, en algún lugar entre xxhdpi y xxxhdpi), por lo que los activos a menudo se escalarán.

Debido a que los activos vectoriales cambian de tamaño con gracia, puede incluir un solo activo, seguro sabiendo que funcionará en todas y cada una de las densidades de pantalla.

Pequeña

Los recursos vectoriales generalmente * son más compactos que los recursos ráster, ya que solo necesita incluir una única versión y porque se comprimen bien.

Por ejemplo, aquí hay un cambio de la aplicación Google I / O donde cambiamos una cantidad de íconos de PNG ráster a vectores y ahorramos 482KB. Si bien esto puede no parecer mucho, esto fue solo para una pequeña iconografía; Las imágenes más grandes (como las ilustraciones) tendrían mayores ahorros.

Esta ilustración, por ejemplo, del flujo de incorporación de la aplicación de E / S de un año anterior, por ejemplo:

Las ilustraciones pueden ser buenos candidatos para vectores

No pudimos reemplazar esto con un VectorDrawable ya que los gradientes no se admitían ampliamente en ese momento (spoiler: ¡ahora lo son!), Así que tuvimos que enviar una versión ráster . Si hubiéramos podido usar un vector, esto habría sido del 30% del tamaño para un mejor resultado:

  • Ráster: tamaño de descarga = 53,9 KB (tamaño de archivo sin procesar = 54,8 KB)
  • Vector: Tamaño de descarga = 3.7 KB (tamaño de archivo sin procesar = 15.8 KB)
Tenga en cuenta que si bien las divisiones de configuración de densidad de Android App Bundle brindan beneficios similares al entregar solo los activos de densidad requeridos en el dispositivo, un VectorDrawable generalmente será más pequeño y también elimina la necesidad de seguir creando activos de trama cada vez más grandes.

Dinámica

A medida que las imágenes vectoriales describen sus contenidos en lugar de "aplanarlos" a píxeles, abren la puerta a nuevas posibilidades interesantes como la animación, la interactividad o la temática dinámica. Más sobre esto en futuras publicaciones.

Los vectores mantienen la estructura de la imagen para que los elementos individuales puedan ser temáticos o animados

Compensaciones

Los vectores tienen algunos inconvenientes que deben considerarse:

Descodificación

Como se indicó anteriormente, los activos vectoriales describen su contenido, por lo tanto, deben inflarse y dibujarse antes de su uso.

Los pasos involucrados en la decodificación de un vector antes de renderizar

Hay dos pasos para esto:

  1. Inflación. Su archivo vectorial debe leerse y analizarse en un VectorDrawable modelando las rutas, grupos, etc. que declara.
  2. Dibujo. Estos objetos modelo deben dibujarse ejecutando comandos de dibujo de Canvas.

Ambos pasos son proporcionales a la complejidad del vector y al tipo de operaciones que realiza. Si usa formas muy complejas, tomará más tiempo analizar esto en un Camino. Del mismo modo, llevará más tiempo realizar más operaciones de dibujo (y algunas son más caras, por ejemplo, operaciones de recorte). Revisaremos esto en una publicación futura de esta serie sobre el perfil de estos costos.

Para vectores estáticos, la etapa de dibujo solo debe realizarse una vez y luego puede almacenarse en caché en un mapa de bits. Los vectores animados no pueden hacer esta optimización ya que sus propiedades necesariamente cambian y requieren un nuevo dibujo.

Compare esto con activos ráster como PNG que solo necesitan decodificar el contenido del archivo, algo que ha sido altamente optimizado con el tiempo.

Esta es la compensación esencial de la trama frente al vector. Los vectores proporcionan los beneficios antes mencionados pero a costa de ser más caros de renderizar. En los primeros días de Android, los dispositivos eran menos potentes y las densidades de pantalla diferían poco. Hoy, los dispositivos Android son más potentes y vienen en una gran variedad de densidades de pantalla. Es por eso que creo que es hora de que todas las aplicaciones se muevan a activos vectoriales.

Idoneidad

Debido a la naturaleza del formato, los vectores son excelentes para describir algunos activos, como iconos simples, etc. Son terribles para codificar imágenes de tipo fotográfico, donde es más difícil describir su contenido como una serie de formas y probablemente sería mucho más eficiente para usar un formato ráster (como webp). Por supuesto, esto es un espectro, dependiendo de la complejidad de su activo.

Conversión

Ninguna herramienta de diseño (que yo sepa) crea VectorDrawables directamente, lo que significa que hay un paso de conversión desde otros formatos. Esto puede complicar el flujo de trabajo entre diseñadores y desarrolladores. Vamos a profundizar en este tema en una publicación futura.

¿Por qué no SVG?

Si alguna vez trabajó con formatos de imagen vectorial, es probable que se haya encontrado con el formato SVG (Gráficos vectoriales escalables), el estándar de la industria en la web. Es capaz y maduro con herramientas establecidas, pero también es un gran estándar. Incluye muchas capacidades complejas como ejecutar JavaScript arbitrario, desenfoque y efectos de filtro o incrustar otras imágenes, incluso gifs animados. Android se ejecuta en dispositivos móviles restringidos, por lo que apoyar la totalidad de las especificaciones SVG no era un objetivo realista.

Sin embargo, SVG incluye una especificación de ruta que define cómo describir y dibujar formas. Con esta API puedes expresar la mayoría de las formas vectoriales. Esto es esencialmente lo que Android admite: las especificaciones de ruta de SVG (más algunas adiciones).

Además, al definir su propio formato, VectorDrawable puede integrarse con las características de la plataforma Android. Por ejemplo, trabajar con el sistema de recursos de Android para hacer referencia a @colors, @dimens o @strings, trabajar con atributos de tema o AnimatedVectorDrawable utilizando Animadores estándar.

Capacidades de VectorDrawable

Como se indicó, VectorDrawable admite las especificaciones de ruta de SVG, lo que le permite especificar una o varias formas para dibujar. Está creado como un documento XML que se ve así:

Tenga en cuenta que debe especificar el tamaño intrínseco del activo, que es el tamaño que tendría si lo configura en un ImageView wrap_content. Los segundos tamaños de ventana gráfica definen el lienzo virtual, o el espacio de coordenadas en el que se definen todos los comandos de dibujo posteriores. Las dimensiones intrínseca y de ventana gráfica pueden diferir (pero deben estar en la misma proporción): si usted define sus vectores en un lienzo 1 * 1, puede definirlos. Realmente quiero.

El elemento contiene uno o muchos elementos . Se pueden nombrar (para referencia posterior, por ejemplo, animación), pero crucialmente deben especificar un elemento pathData que describa la forma. Esta cadena de aspecto críptico puede considerarse como una serie de comandos que controlan un lápiz en un lienzo virtual:

Visualizar operaciones de ruta

Los comandos anteriores mueven el lápiz virtual, luego dibujan una línea a otro punto, levantan y mueven el lápiz, luego dibujan otra línea. Con solo los 4 comandos más comunes, podemos describir prácticamente cualquier forma (hay más comandos para ver las especificaciones):

  • M mover a
  • Línea L a
  • Curva C (bezier cúbico) a
  • Z cerca (línea al primer punto)

(Los comandos en mayúscula usan coordenadas absolutas y las minúsculas usan relativo)

Quizás se pregunte si necesita preocuparse por este nivel de detalle. ¿No lo obtiene de los archivos SVG? Si bien no necesita poder leer una ruta y comprender lo que dibujará, tener una comprensión básica de lo que hace un VectorDrawable es extremadamente útil y necesario para comprender algunas de las funciones avanzadas que veremos más adelante.

Los caminos en sí mismos no dibujan nada, necesitan ser trazados y / o rellenados.

La Parte 2 de esta serie entra en más detalles sobre las diferentes formas de rellenar / trazar caminos.

También puede definir grupos de caminos. Esto le permite definir transformaciones que se aplicarán a todas las rutas dentro del grupo.

Tenga en cuenta que no puede rotar / escalar / traducir rutas individuales. Si desea este comportamiento, deberá colocarlos en un grupo. Estas transformaciones tienen poco sentido para las imágenes estáticas que podrían "hornearlas" directamente en sus caminos, pero son extremadamente útiles para la animación.

También puede definir rutas de recorte, es decir, enmascarar el área a la que pueden dibujar otras rutas en el mismo grupo. Se definen exactamente de la misma manera que los caminos.

Una limitación de la nota es que las rutas de clip no tienen suavizado.

Demostración de una ruta de clip sin suavizado

Este ejemplo (que tuve que ampliar mucho para mostrar el efecto) muestra dos enfoques para dibujar un ícono de obturador de cámara. El primero dibuja los caminos, el segundo dibuja un cuadrado sólido, enmascarado a la forma del obturador. El enmascaramiento puede ayudar a crear efectos interesantes (especialmente cuando está animado), pero es relativamente costoso, así que si puedes evitarlo dibujando una forma de una manera diferente, entonces hazlo.

Los caminos se pueden recortar; eso es solo dibujar un subconjunto de la ruta completa. Puede recortar caminos llenos, ¡pero los resultados pueden ser sorprendentes! Es más común recortar trazos trazados.

Recortar caminos

Puede recortar desde el principio o el final de una ruta o aplicar un desplazamiento a cualquier recorte. Se definen como una fracción de la ruta [0,1]. Vea cómo establecer diferentes valores de recorte cambia la parte de la línea que se dibuja. También tenga en cuenta que las compensaciones pueden hacer que los valores de recorte se "envuelvan". Una vez más, esta propiedad no tiene mucho sentido para las imágenes estáticas, pero es útil para la animación.

El elemento del vector raíz admite una propiedad alfa [0, 1]. Los grupos no tienen una propiedad alfa, pero las rutas individuales admiten fillAlpha / strokeAlpha.

Declarar la independencia

Esperemos que esta publicación le dé una idea de qué son los activos vectoriales, sus beneficios y compensaciones. El formato vectorial de Android es capaz y tiene un amplio soporte. Dada la variedad de dispositivos en el mercado, el uso de activos vectoriales debería ser su opción predeterminada, recurriendo solo a rásteres en casos especiales. Únase a nosotros en las próximas publicaciones para obtener más información:

Próximamente: creación de activos vectoriales para Android
Próximamente: Perfiles de Android VectorDrawables