Qué es VectorDrawable, el formato de imagen vectorial de Android
viernes, 28 de diciembre de 2018
Hay dispositivos Android de diferentes tamaños, formas y densidades de pantalla. Por ese motivo, me encanta usar recursos vectoriales de resolución independiente. ¿Pero qué son exactamente? ¿Cuáles son sus ventajas? ¿Cuáles son los costos? ¿Cuándo debo usarlos? ¿Cómo se crean y se utilizan? En esta serie de publicaciones, intentaré explorar estas preguntas y explicar no solo por qué creo que la gran mayoría de los recursos de tus apps deberían ser vectoriales, sino también cómo aprovecharlos al máximo.
Tramas contra vectores
La mayoría de los formatos de imagen (png, jpeg, bmp, gif, webp, etc.) son recursos de trama; es decir, describen la imagen como una cuadrícula fija de píxeles. Por ello, se establecen en una resolución específica y no están relacionados con su contenido, sino con 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é debería usar vectores?
Los recursos vectoriales tienen 3 ventajas principales:- Son nítidos.
- Son pequeños.
- Son dinámicos.
Nitidez
Las imágenes vectoriales pueden redimensionarse sin problemas: como describen la imagen en un tamaño de lienzo abstracto, es posible achicar o agrandar el lienzo, y luego volver a dibujar la imagen en ese tamaño. Sin embargo, los recursos de trama pueden deteriorarse cuando se les cambia el tamaño. Si bien no suele haber problemas al achicar los recursos de trama (dado que se descarta información), agrandarlos puede generar artefactos borrosos o con bandas de colores, ya que es necesario interpolar los píxeles faltantes.Esta es la razón por la que en Android necesitamos proporcionar varias versiones de cada recurso de trama para diferentes densidades de pantalla:
- res/drawable-mdpi/foo.png
- res/drawable-hdpi/foo.png
- res/drawable-xhdpi/foo.png
- …
Dado que los recursos vectoriales se redimensionan sin problema, puedes incluir un solo elemento sabiendo que funcionará en cualquier densidad de pantalla.
Tamaño reducido
Por lo general*, los recursos vectoriales son más compactos que los de trama debido a que solo incluyen una única versión y se comprimen sin problema.Por ejemplo, aquí podemos observar un cambio de la app de Google I/O: modificamos varios íconos de trama en formato PNG a vectores y nos ahorramos 482 KB. Si bien es una cifra insignificante, esto fue solo en un elemento pequeño. En las imágenes más grandes (como ilustraciones), se podrá ahorra muchísimo más espacio.
Veamos la siguiente ilustración del flujo de integración de la app de Google I/O del año pasado:
Como no pudimos reemplazar esto con un elemento VectorDrawable, ya que las gradientes no eran compatibles en ese momento (¡pero ahora sí lo son!), tuvimos que usar una versión de trama 😔. Si hubiésemos podido utilizar un vector, nos hubiéramos ahorrado un 30% del tamaño con una calidad mucho mejor :
- Trama: tamaño de descarga = 53.9 KB (tamaño del archivo sin procesar = 54.8 KB)
- Vector: tamaño de descarga = 3.7 KB (tamaño del archivo sin procesar = 15.8 KB)
Cabe destacar que si bien las divisiones de configuración de densidad de Android App Bundle ofrecen beneficios similares publicando solo los recursos de densidad que requiere un dispositivo, los elementos VectorDrawable suelen ser más pequeños y no requieren que se sigan creando recursos de trama cada vez más grandes.
Dinamicidad
Como las imágenes vectoriales describen su contenido en lugar de "apisonarlos" en píxeles, abren las puertas a nuevas e interesantes posibilidades, como la animación, la interactividad o la temática dinámica. Abordaremos este tema con más detalles en publicaciones futuras.Desventajas
Asimismo, los vectores tienen algunos puntos en contra que también es necesario tener en cuenta:Descodificación
Como se indicó anteriormente, los recursos vectoriales describen su contenido, por lo que deben ampliarse y dibujarse antes de su uso.Ese proceso implica dos pasos:
- Ampliación: Se debe leer y analizar archivo vectorial en un elemento VectorDrawable que modele las rutas, los grupos, etc. que declares.
- Dibujo: Estos objetos de modelo deben dibujarse ejecutando los comandos de dibujo de Canvas.
En el caso de los vectores estáticos, la etapa de dibujo solo debe realizarse una vez y luego puede almacenarse en caché en un Bitmap. Los vectores animados no pueden realizar esta optimización, ya que sus propiedades cambian y deben volver a dibujarse.
Compara esto con recursos de trama (como PNG), que solo necesitan decodificar el contenido del archivo, un proceso que se ha ido optimizando con el paso del tiempo.
Esta es la mayor desventaja de los recursos de trama contra los vectoriales. Estos últimos brindan los beneficios que mencioné en primer lugar, pero teniendo en cuenta que su renderizado es más costoso. Cuando recién salió Android, los dispositivos eran menos potentes y las densidades de pantalla diferían muy poco. Hoy en día, son más potentes y están disponibles en una gran variedad de densidades de pantalla. Por eso, creo que es hora de que todas las apps comiencen usar recursos vectoriales.
Idoneidad
Debido a la naturaleza del formato, los vectores son una gran herramienta para describir algunos recursos, como íconos simples, etc. No son buenos codificando 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 conveniente usar un formato de trama (como webp). Esto es, por supuesto, un espectro, ya que depende de la complejidad de tus recursos.
Conversión
Ninguna herramienta de diseño (que conozca) crea VectorDrawables de forma directa, lo que significa que hay un paso de conversión desde otros formatos, lo que podría complicar el flujo de trabajo entre diseñadores y desarrolladores. Ahondaremos en este tema en una publicación futura.¿Por qué no se deben usar archivos SVG?
Si alguna vez trabajaste con formatos de imágenes vectoriales, es probable que te hayas encontrado con el formato SVG (gráficos vectoriales escalables), el estándar de la industria en la Web. Si bien este formato es ideal para usar con herramientas establecidas, también es un estándar vasto . Incluye muchas funciones complejas, como ejecutar código JavaScript arbitrario, efectos de desenfoque y filtro, o incorporar otras imágenes, incluidos GIF animados. Como Android se ejecuta en dispositivos móviles restringidos, admitir la totalidad de la especificación de SVG no es un objetivo realista.Sin embargo, este formato 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. Básicamente, esto es lo que admite Android: la especificación de ruta del archivo SVG (además de algunas funciones).
Asimismo, al definir su propio formato, VectorDrawable puede integrarse con las funciones de la plataforma Android (por ejemplo, a fin de trabajar con el sistema de recursos de Android para hacer referencia a @colors, @dimens o @strings, o bien con atributos de tema o AnimatedVectorDrawable usando Animators estándar).
Funciones de VectorDrawable
Como ya expliqué, VectorDrawable admite la especificación de ruta de SVG, lo que permite especificar una o varias formas para dibujar. Se crea como un documento XML que se ve así:Cabe destacar que es necesario especificar el tamaño intrínseco del recurso, que es el que tendría si lo configuraras en un wrap_content ImageView. Los tamaños de la segunda ventana gráfica definen el lienzo virtual o el espacio de coordenadas en el que se establecen todos los comandos de dibujo posteriores. Las dimensiones intrínsecas y de la ventana gráfica pueden diferir (aunque deberían estar en la misma proporción). Si quieres, puedes definir tus vectores en un lienzo de 1*1.
El elemento <vector> contiene uno o varios elementos <path>. Si bien puedes asignarles un nombre (a modo de referencia, como "animación"), deben especificar un elemento pathData que describa la forma. Esta string de aspecto críptico puede considerarse como una serie de comandos que controlan una pluma en un lienzo virtual:
Los comandos anteriores mueven la pluma virtual, luego dibujan una línea hacia otro punto, levantan y mueven la pluma y, por último, dibujan otra línea. Usando solo los 4 comandos más comunes podemos describir casi cualquier forma (hay más comandos; consulta las especificaciones):
- M mover a
- L trazar una línea a
- C (Bézier cúbica) trazar una curva a
- Z cerrar (línea al primer punto)
Posiblemente te preguntes si debes preocuparte por este nivel de detalle. ¿Los archivos SVG no aportan los mismos beneficios? Si bien no es imprescindible saber leer una ruta y comprender lo que dibujará, tener una comprensión básica de lo que hace VectorDrawable es extremadamente útil y necesario para comprender algunas de las funciones avanzadas que veremos más adelante.
Por sí solos, las ruta no dibujan nada. Se las debe trazar o rellenar.
La parte 2 de esta serie explica más detalladamente las diferentes formas de rellenar o trazar rutas.
También puedes establecer grupos de rutas, lo que te permite definir transformaciones que se aplicarán a todas las rutas del conjunto.
Ten en cuenta que no es posible rotar, ajustar a escala ni traducir rutas individuales. Si quieres realizar algunas de esas acciones, deberás colocarlas en un grupo. Esta transformación no tiene mucho sentido para las imágenes estáticas que podrían "procesarse" directamente en sus rutas, pero son muy útiles para la animación.
También puedes definir rutas de recorte; es decir, enmascarar el área a la que pueden dibujar otras rutas del mismo grupo . Se definen de la misma manera que las rutas convencionales.
Una de las limitaciones que las rutas de recorte no pueden suavizarse.
Este ejemplo (que tuve que ampliar a fin de que se vea el efecto) muestra dos enfoques para dibujar el ícono de obturador de una cámara. El primero dibuja las trayectorias, mientras que el segundo traza un cuadrado sólido, enmascarado a la forma del obturador. El enmascaramiento puede ayudar a crear efectos interesantes (especialmente cuando están animados), pero es un proceso costoso, por lo que si puedes evitarlo dibujando una forma de otra manera, hazlo.
Las rutas se pueden recortar; es decir, dibujar solamente un subconjunto de la ruta completa. Puedes recortar rutas rellenas, aunque los resultados podrían ser inesperados. Es más común recortar rutas trazadas.
Puedes recortar desde el inicio o el final de una ruta, o bien aplicar un desplazamiento a cualquier recorte. Se definen como una fracción de la trayectoria [0,1]. Mira cómo configurar diferentes valores de recorte cambia la parte de la línea que se dibuja. También ten en cuenta que las compensaciones pueden hacer que se "envuelvan" los valores de recorte. Una vez más, esta propiedad no tiene mucho sentido para las imágenes estáticas, pero es útil para las animadas.
El elemento vector raíz admite una propiedad Alfa [0, 1]. Los grupos no tienen una propiedad de este tipo, pero las rutas individuales admiten fillAlpha/strokeAlpha.
Declaración de la independencia
Espero que esta publicación te dé una idea de qué son los recursos vectoriales, junto con sus aspectos positivos y negativos. El formato vectorial de Android es muy funcional y tiene un campo de compatibilidad amplísimo. Dada la variedad de dispositivos en el mercado, el uso de recursos vectoriales debería ser tu opción predeterminada, y solo recurrir a tramas en casos especiales. Únete a nosotros en las siguientes publicaciones para obtener más información:Próximamente: Cómo dibujar elementos Path
Próximamente: Cómo crear recursos vectoriales para Android
Próximamente: Cómo usar recursos vectoriales en apps de Android
Próximamente: Cómo utilizar VectorDrawables en Android