Regular readers of Styling Android will know of my love of VectorDrawable and AnimatedVectorDrawable. While (at the time of writing) we’re still waiting for VectorDrawableCompat so we can only use them on API 21 (Lollipop) and later. However, the release of Android Studio 1.4 has just added some backwards compatibility to the build tools so we can actually begin to use VectorDrawable for pre-Lollipop. In this article we’ll take a look at how this works.
Before we begin let’s have a quick recap of what VectorDrawable is. Essentially it is an Android wrapper around SVG path data. SVG paths are a way of specifying complex graphical elements in a declarative way. They are particularly suited to line drawings and vector graphics, and unsuitable for photographic images. Traditionally in Android we have ShapeDrawable where we can do some basic stuff but often we have to convert vector and line graphics in to bitmaps at various pixel densities in order to use them.
Android Studio 1.4 introduces the ability to import SVG graphics into Android Studio and converts them automatically to VectorDrawable. These can be icons from the material icons pack or standalone SVG files. Importing material icons works flawlessly and provides a large and rich set of icons. However, importing standalone SVG files can be rather more problematic. The reason for this is that the VectorDrawable format only supports a subset of SVG and is missing features such as gradient and pattern fills, local IRI references (the ability to give an element a unique reference and re-use it within the SVG via that reference), and transformations – which are all commonly used.
For example, even a relatively simple image such as the official SVG logo (below) fails to import because it uses local IRI references:
It’s currently unclear whether these omissions are for performance reasons (for example, gradients can be complex to render) or are future developments.
With an understanding the SVG format (which is beyond the scope of this article) it is possible to manually tweak the logo to remove the local IRI references and this is identical to the one above:
This still does not import and the error message of “premature end of file” gives little clue where the problem lies. Thanks to a suggestion from Wojtek Kaliciński I changed the width and height from percentage values to absolute values and the import now worked. However because translations are not supported all of the elements were positioned badly:
By manually applying the all of the translation and rotation transformations from the original file (by wrapping
elements which support transformations) I was able to actually get the official SVG logo to import and render correctly as a VectorDrawable on Marshmallow:
There is a conversion tool by Juraj Novák which will convert SVG directly to VectorDrawable. It has many of the same restrictions of not handling gradients and local IRI references, but does a much better job of converting my hand-tweaked SVG. It was not thrown by having percentage width and height values, and it has an experimental mode to apply transformations which worked well in this case. But the need to manually convert the local IRI references still required hand-tweaking of the raw SVG files.
By dropping this in our
res/drawable folder we can now reference it as any drawable:
<?xml version="1.0" encoding="utf-8"?>
Provided that we are using gradle plugin 1.4.0 or later (at the time of writing this isn’t released but
1.4.0-beta6 does the trick) this will now work back to API 1!
So what’s happening? If we take a look in the generated code in the build folder it becomes obvious:
For API 21 and later the XML vector drawable that we imported is used, but for earlier devices a PNG of the vector drawable is used instead.
But what if we decide that we don’t need all of these densities and are concerned about the increased size of our APK as a result of this? We can actually control which densities will be generated using the
generatedDensities property of the build flavor:
apply plugin: 'com.android.application'
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
compile fileTree(dir: 'libs', include: ['*.jar'])
If we now build (remember to clean first to remove the resources that were generated by previous builds) we can see this only creates the densities we’ve specified:
So, let’s now actually take a look at what is produced in the png representation:
This is essentially the same as how the imported SVG was rendered before I manually added the missing transformations. I should mention that there is a lint warning which indicates that
elements are not supported for raster image generation, but that does not detract from the fact that VectorDrawable is an Android-specific format so not fully supporting it seems baffling.
We now beginning to understand why transformations are not supported by the import tool – because transformations on VectorDrawable
elements is not supported when converting VectorDrawable to a raster image for backwards compatibility. This would appear to be a major omission: Totally valid VectorDrawable assets which render perfectly under Lollipop and later do not actually render correctly when converted to PNG.
To, to summarise: If you use these new tools to import assets from the material icons library they work flawlessly. However, it seems misleading to even claim that the import tool is actually capable of importing SVG when it only supports a very limited subset, and will not correctly import most real-world SVG files. Moreover the lack of support even for the full VectorDrawable specification in the VectorDrawable -> raster image conversion makes the implementation feel unfinished and not really ready for general use.
For the level of manual tweaking that I was required to do to even get the official SVG logo to even be converted to a VectorDrawable by the import tool it would not have required much more work to manually convert it to a VectorDrawable and completely bypass the import tool altogether. Although I would still be required to manually apply my transformations to all of the coordinates within the SVG pathData elements in order to manually apply the necessary transformations.
Let’s hope that some of these issues are addressed soon so that these new potentially very useful new tools begin to fulfil some of their promise.
The source code for this article is available here.
© 2015, Mark Allison. All rights reserved.
Vectors For All (almost) by Styling Android is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Permissions beyond the scope of this license may be available at http://blog.stylingandroid.com/license-information.