Stop using your SVG as PNG

David Blanc
AndroidPub
Published in
4 min readNov 18, 2017

When it comes to handling graphical resources, the Android platform has a few tricks up its sleeves. Among those, the use of 9-patch PNG and SVG images are quite powerful tools.

On one hand, 9-patch PNGs are quite specific to Android. They allow you to specify how to resize a certain image by defining which zones can be stretched and which zones should be preserved. They even allow you to include a padding for the content. With such an interesting tool, it’s quite a shame that so few UI designers know they exist, let alone how they work. Maybe that’s the reason why lots of Android developers tend to use XML drawables for quite a few use-cases where they would be perfectly appropriate (think plain button background with rounded corners for instance).

On the other hand, SVG are more universal, but their support on Android took some time to arrive (and is not perfect yet). SVG is an XML based vector graphic format, and it comes with two great benefits:

  • as a vector format (as opposed to bitmaps), it allows you to include only one version of your images, and the result will be adapted to the needed size and resolution wherever they’re used. No need to provide all those mdpi, hdpi, xhdpi… versions for your icons and other images.
  • as an XML based format, the vector drawable resources you put in your Android app can use dynamic coloring by using your @color/my_favorite_color variables instead of hex RGB constants. That can be quite handy when you have several build flavors with different color schemes: no need to generate images for each variant, just use the Android color parameters inside the XML.

But as I just stated, it took some time for the Android platform to offer a proper way to handle them (some would say we’re not really there yet, but we’ve definitely made progress over the past few versions). SVG graphics started being supported in Android 5.0 (API 21). That was great news, but what about older devices? Google provided a very simple workaround: PNG versions of your SVG are generated when you compile your app, and SVG are moved to an API21 folder. This allows you to use the SVG files on recent devices, and their PNG versions on older ones. This solution was far from perfect. It did not allow you to really harness all the power of SVG files into your app, but at least, your favorite UI designer didn’t have to provide 5 different versions of each image for you to put in the appropriate folders.

Then Google decided to look for a better solution, which was achieved through the AppCompat library. By adding a little line to your gradle build file, you could choose not to generate those alternate PNG versions anymore and instead let the AppCompat library handle those files. This also implied that you had to use a special attribute in your layouts: app:srcCompat, instead of android:src in your ImageViews. So what about all the other attributes like background or drawableLeft/Right? How do you get those to work? Well, thanks to the new wonderful ConstraintLayout, you can easily replace these with a simple ImageView and set its constraints to match the corresponding view. So SVG is now properly supported, can scale without pixelation and even use the color parameters.

The big problem here is that this new support is somewhat incompatible with the previous compatibility mode: since additional PNG are not generated, your app will definitely crash if you use SVG like they’re PNG and someone runs it on an old device. So those who had incorporated SVG in their app for a while are faced with difficult dilemma:

  • Embrace the new SVG support fully, which implies they have to change all their layouts to be sure they don’t have a src, background, drawableLeft or other attribute pointing directly at an SVG resource. When you’ve been adding SVG for a year or two, changing it everywhere can be a daunting task.
  • Or keep using the old way and miss all these new superpowers.

Developers being what they are, they’re rarely happy with this kind of situations and they tried to find workarounds to facilitate the transition, like David Ferrand described. None of those options were perfect, but one in particular is still problematic. That’s the situation I was in a few days ago, until I thought about one last option that seem to be missing for David’s graph: convert those SVG to PNG, and startover with a clean slate! Or to be more precise: you can do manually for all those old SVG files what the compiler used to do automatically for you, and be done with it. Then you can switch effortlessly to AppCompat support and start using any new SVG properly. And it’s much easier to do than it sounds.

  • Start by doing one last compilation of your app based on the old SVG support.
  • Then go to your build folder and look for the resources directory. You will see your resources sorted by resolution (as PNG files) and API (as SVG, for API 21 and above).
  • Just copy all those resources directory to your project to replace the previous drawable folders you had.
  • Now you can switch the AppCompat support on in your gradle config file. PNG files won’t be generated for SVG anymore, but since you included the PNG versions of your files, it doesn’t matter!

This only takes a few minutes to do and you can be sure you don’t add any regression to your app by doing it. So from now on, you can carefully use the srcCompat attribute to reference SVG files when you add new layouts. Just don’t forget to gradually convert your old layouts to the new way so you can eventually delete all those useless PNG!

--

--

David Blanc
AndroidPub

Lead Android developer at @InformatiqueBP. I love Android, Kotlin and coding in general. @speekha on Twitter. Author of HttpMocker