Today's #AndroidDesign #Protip from +Roman Nurik is about aiming for fewer PNGs and more XML.

We review a lot of apps here on the Android Developer Relations team. As part of that review, sometimes we dig into APKs (particularly the manifest and resources) to see what goodies lie within. Now, we've seen everything from several megs of In-App billing documentation (yes, HTML, Javascript, the works) for wait for it—another platform (!!) sitting in the APK root all the way to hundreds of button images with blurry pre-baked text sitting in the density-disregarding res/drawable folder. 

The one thing we see too much of is loads of PNG files that don't need to be there. We've really got to start thinking carefully about alternative ways to deliver the same UI. And remember, the Holo visual language is all about letting spacing, typography and dynamic images do the work, rather than the often unnecessary static UI elements that churn down your data/pixel ratio [1].

With that, let's look at a couple common examples where PNGs in your app binary could be replaced with something else:

Simple shapes: If all you need is a simple shape, like a circle mask for a contact avatar, or a simple circular button, just use Android's shape XML drawables [2]. It's quick and easy. Here's an example: http://goo.gl/30ajpA

Gradients: Oftentimes you'll find yourself using black-to-transparent gradients for background protection of text on images. Shape XML works great for this as well. Here's an example of that: http://goo.gl/Adh1lz

Large nine-patches: First, if you haven't heard of nine-patches [3], stop what you're doing and go read about them immediately! Now, if your .9.png files have large stretch regions, see if you can shrink them. The PNG file format does a decent job optimizing repetition like this anyway, but not always. The Android Asset Studio's nine-patch generator [4] has a "trim" option that trims excess stretch regions. Be careful with very small (1px) regions though if you're relying on platform scaling to lower densities.

"Pressed" state button images: While it's critical that users see a visual change when pressing an interactive element such as a button, you can often save the trouble of creating additional PNGs just for this purpose. Layer list drawables [5] let you overlay several drawables on top of one another. If the bottom drawable is your default-state button PNG and the top is a state list drawable that's transparent by default and a semitransparent white/black when pressed, the effect will be that your default-state button is lightened/darkened on press. Here's an example from the I/O 2013 app: http://goo.gl/Xcx1nk

There are of course tons of other possible ways to remove PNGs, and these can often be combined. For example, sometimes you'll want to add a bit of noise over your gradients to minimize banding artifacts. That's simple: a layer-list drawable containing a gradient shape drawable and a <bitmap> [6] drawable referencing a small "noise" PNG tile with a tileMode set to repeat will do the trick.

To wrap up, I'd recommend the following exercise. Open up your APK (it's just a ZIP file!), or your project tree, and take a long hard look at the PNGs in your drawable directories. Can you replace any of them with XML drawables, or even custom drawing code? And better yet, do you even really need them in your UI, or are they unjustifiably lowering your data/pixel ratio?

Have similar tips and tricks? Leave 'em in the comments!

[1] Minimalist interface design and the data-pixel ratio https://medium.com/design-ux/49df9dcd997a
[2] Shape drawables http://goo.gl/QV5P
[3] Nine-patches http://goo.gl/zGxf4B
[4] Nine-patch generator http://goo.gl/QBjiX
[5] Layer list drawables http://goo.gl/SXqNlb
[6] Bitmap with tileMode http://goo.gl/sz3qVH
Shared publiclyView activity