Vectors For All (almost)



id="twttrHubFrameSecure" allowtransparency="true" frameborder="0" scrolling="no" tabindex="0" name="twttrHubFrameSecure" src="https://platform.twitter.com/widgets/hub.e00c464da75b252cb1a028400793cadc.html" style="margin: 0px; padding: 0px; border-width: 0px; border-style: initial; font-size: 14px; vertical-align: baseline; max-width: 100%; color: rgb(68, 68, 68); font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 14px; position: absolute; top: -9999em; width: 10px; height: 10px;">

Styling Android

A technical guide to improving the UI and UX of Android apps

Skip to content

Vectors For All (almost)

Regular readers of Styling Android will know of my love of VectorDrawable andAnimatedVectorDrawable. While (at the time of writing) we’re still waiting forVectorDrawableCompat 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.

width="468" height="60" frameborder="0" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" id="aswift_0" name="aswift_0" style="margin: 0px 0px 1.71429rem; padding: 0px; border-width: 0px; border-style: initial; vertical-align: baseline; max-width: 100%; left: 0px; position: absolute; top: 0px;">

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 packor 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:

svg_logo

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:

svg_logo2

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:

svg_logo2

By manually applying the all of the translation and rotation transformations from the original file (by wrapping elements in elements which support transformations) I was able to actually get the official SVG logo to import and render correctly as a VectorDrawable on Marshmallow:

SVGLogo

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:

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:

Screen Shot 2015-10-03 at 15.20.33

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:

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:

Screen Shot 2015-10-03 at 15.27.08

So, let’s now actually take a look at what is produced in the png representation:

svg_logo2

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 convertingVectorDrawable 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 VectorDrawablespecification 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. This article originally appeared on Styling Android.

Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License

width="468" height="60" frameborder="0" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" id="aswift_1" name="aswift_1" style="margin: 0px 0px 1.71429rem; padding: 0px; border-width: 0px; border-style: initial; vertical-align: baseline; max-width: 100%; left: 0px; position: absolute; top: 0px;">
id="twitter-widget-0" scrolling="no" frameborder="0" allowtransparency="true" class="twitter-share-button twitter-share-button-rendered twitter-tweet-button" title="Twitter Tweet Button" src="https://platform.twitter.com/widgets/tweet_button.e00c464da75b252cb1a028400793cadc.en.html#_=1447810447833&count=none&dnt=false&id=twitter-widget-0&lang=en&original_referer=https%3A%2F%2Fblog.stylingandroid.com%2Fvectors-for-all-almost%2F&related=AddToAny%2Cmicropat&size=m&text=Vectors%20For%20All%20(almost)&type=share&url=http%3A%2F%2Fblog.stylingandroid.com%2Fvectors-for-all-almost%2F" data-url="http://blog.stylingandroid.com/vectors-for-all-almost/" style="margin: 0px; padding: 0px; border-width: 0px; border-style: initial; max-width: none; display: inline; position: absolute; visibility: hidden; width: 0px; height: 0px; vertical-align: baseline !important;">Share

CC BY-NC-SA 4.0 
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.

This entry was posted in Android StudioDrawableVectorDrawable and tagged androiddev on October 16, 2015.

Post navigation

 Data Binding – Part 5Material ProgressBar 

Leave a Reply

Your email address will not be published. Required fields are marked *

CAPTCHA Image
Refresh Image

*

 

 

name="fb_xdm_frame_https" frameborder="0" allowtransparency="true" allowfullscreen="true" scrolling="no" title="Facebook Cross Domain Communication Frame" aria-hidden="true" tabindex="-1" id="fb_xdm_frame_https" src="https://s-static.ak.facebook.com/connect/xd_arbiter/TlA_zCeMkxl.js?version=41#channel=f15d70aed8&origin=https%3A%2F%2Fblog.stylingandroid.com" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; vertical-align: baseline; max-width: 100%;">
<iframe name="oauth2relay2011385910" id="oauth2relay2011385910" src="https://accounts.google.com/o/oauth2/postmessageRelay?parent=https%3A%2F%2Fblog.stylingandroid.com#rpctoken=249812384&forcesecure=1" tabindex="-1" style="margin: 0px; padding: 0px; border-width: 0px; border-style: initial; font-size: 14px; vertical-align: baseline; max-width: 100%; color: rgb(68, 68, 68); font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 14px; width: 1px; height: 1px; position: absolute; top: -100px;"></iframe>
id="twttrHubFrameSecure" allowtransparency="true" frameborder="0" scrolling="no" tabindex="0" name="twttrHubFrameSecure" src="https://platform.twitter.com/widgets/hub.e00c464da75b252cb1a028400793cadc.html" style="margin: 0px; padding: 0px; border-width: 0px; border-style: initial; font-size: 14px; vertical-align: baseline; max-width: 100%; color: rgb(68, 68, 68); font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 14px; position: absolute; top: -9999em; width: 10px; height: 10px;">

Vectors For All (almost)

Regular readers of Styling Android will know of my love of VectorDrawable andAnimatedVectorDrawable. While (at the time of writing) we’re still waiting forVectorDrawableCompat 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.

width="468" height="60" frameborder="0" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" id="aswift_0" name="aswift_0" style="margin: 0px 0px 1.71429rem; padding: 0px; border-width: 0px; border-style: initial; vertical-align: baseline; max-width: 100%; left: 0px; position: absolute; top: 0px;">

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 packor 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:

svg_logo

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:

svg_logo2

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:

svg_logo2

By manually applying the all of the translation and rotation transformations from the original file (by wrapping elements in elements which support transformations) I was able to actually get the official SVG logo to import and render correctly as a VectorDrawable on Marshmallow:

SVGLogo

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:

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:

Screen Shot 2015-10-03 at 15.20.33

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:

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:

Screen Shot 2015-10-03 at 15.27.08

So, let’s now actually take a look at what is produced in the png representation:

svg_logo2

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 convertingVectorDrawable 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 VectorDrawablespecification 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. This article originally appeared on Styling Android.

Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License

width="468" height="60" frameborder="0" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" id="aswift_1" name="aswift_1" style="margin: 0px 0px 1.71429rem; padding: 0px; border-width: 0px; border-style: initial; vertical-align: baseline; max-width: 100%; left: 0px; position: absolute; top: 0px;">
id="twitter-widget-0" scrolling="no" frameborder="0" allowtransparency="true" class="twitter-share-button twitter-share-button-rendered twitter-tweet-button" title="Twitter Tweet Button" src="https://platform.twitter.com/widgets/tweet_button.e00c464da75b252cb1a028400793cadc.en.html#_=1447810447833&count=none&dnt=false&id=twitter-widget-0&lang=en&original_referer=https%3A%2F%2Fblog.stylingandroid.com%2Fvectors-for-all-almost%2F&related=AddToAny%2Cmicropat&size=m&text=Vectors%20For%20All%20(almost)&type=share&url=http%3A%2F%2Fblog.stylingandroid.com%2Fvectors-for-all-almost%2F" data-url="http://blog.stylingandroid.com/vectors-for-all-almost/" style="margin: 0px; padding: 0px; border-width: 0px; border-style: initial; max-width: none; display: inline; position: absolute; visibility: hidden; width: 0px; height: 0px; vertical-align: baseline !important;">Share

CC BY-NC-SA 4.0 
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.

Leave a Reply

Your email address will not be published. Required fields are marked *

CAPTCHA Image
Refresh Image

*

 

 

SEARCH STYLING ANDROID

 

CATEGORIES

ARCHIVES

width="250" height="250" frameborder="0" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" id="aswift_2" name="aswift_2" style="margin: 0px; padding: 0px; border-width: 0px; border-style: initial; vertical-align: baseline; max-width: 100%; left: 0px; position: absolute; top: 0px;">
name="fb_xdm_frame_https" frameborder="0" allowtransparency="true" allowfullscreen="true" scrolling="no" title="Facebook Cross Domain Communication Frame" aria-hidden="true" tabindex="-1" id="fb_xdm_frame_https" src="https://s-static.ak.facebook.com/connect/xd_arbiter/TlA_zCeMkxl.js?version=41#channel=f15d70aed8&origin=https%3A%2F%2Fblog.stylingandroid.com" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; vertical-align: baseline; max-width: 100%;">
<iframe name="oauth2relay2011385910" id="oauth2relay2011385910" src="https://accounts.google.com/o/oauth2/postmessageRelay?parent=https%3A%2F%2Fblog.stylingandroid.com#rpctoken=249812384&forcesecure=1" tabindex="-1" style="margin: 0px; padding: 0px; border-width: 0px; border-style: initial; font-size: 14px; vertical-align: baseline; max-width: 100%; color: rgb(68, 68, 68); font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 14px; width: 1px; height: 1px; position: absolute; top: -100px;"></iframe>
發佈了6 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章