VectorDrawable

VectorDrawable PNG

Or The Curious Case of the Disappearing Vectors

Regular readers of Styling Android may remember a post a couple of months ago which dealt with some strange renderings of VectorDrawables because of fill winding issues. Recently I encountered another issue with some puzzling rendering of a VectorDrawable, and in this post we’ll take a look at some potential gotchas.

Firstly a little background. On the project where I encountered the issue we’re going to look at we’d recently made the decision to drop VectorDrawableCompat from the project. The reasoning for this was that we had quite a complex AnimatedVectorDrawable which was rendering perfectly when using native AnimatedVectorDrawable, but when we switched to the Compat version the animation wasn’t running on some Lollipop devices (which had worked perfectly well natively). Simple AnimatedVectorDrawables worked fine, but the more complex one did not – my guess was memory issues when using the Compat library. So a decision was made to keep the animation for Lollipop+ users, but use a static image for earlier devices (which actually constituted a really small percentage of the user-base for the app).

To achieve this, we turned off the VectorDrawable support library in our build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.0"
    defaultConfig {
        applicationId "com.stylingandroid.vectordrawablepng"
        minSdkVersion 14
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        vectorDrawables.useSupportLibrary = false;
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile 'com.android.support:appcompat-v7:25.0.0'
    compile 'com.android.support.constraint:constraint-layout:1.0.0-beta3'
}

By doing this we would default back to the older behaviour which is built in to the build toolchain: Converting our VectorDrawable assets to PNGs during the build and using these on pre-Lollipop devices. All was going well until we started getting defect reports that some, but not all, of our VectorDrawable assets had disappeared on pre-Lollipop devices. The problem turned out to be really easy to fix, but it took some head scratching to actually work out what was going wrong.

For the sample code I have simply imported one of the material icons in the Android Studio Vector Asset import tool, and then manually tweaked the colouring.

If this is rendered on a Nougat device then everything is good:

problem1-nougat

But the problem becomes apparent if we render it on a KitKat device:

problem1-kitkat

So what on earth is going on? In order to understand the issue we need to look at the actual PNG asset that has been generated during the build:

generated-png

The PNG that was generated is actually white. The reason that it is disappearing is because we are drawing a white asset over a white background and it is invisible as a result. So why is it rendering as green on a Nougat device, but the generated PNG is white? To understand that let’s take a look at at the VectorDrawable asset itself:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
  android:width="128dp"
  android:height="128dp"
  android:tint="#1E9618"
  android:viewportHeight="24.0"
  android:viewportWidth="24.0">
  <path
    android:fillColor="#FFFFFF"
    android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z" />
</vector>

To get the colouring we want we’re drawing the vector path using a white fill colour and then applying a tint to the correct shade of green in the parent vector element. This works fine when we render as a VectorDrawable even on KitKat when using the Compat library. However when we generate PNGs during the build the tint value is not applied.

So the fix is actually really simple, just use the correct android:fillColor value, and ditch the android:tint:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
  android:width="128dp"
  android:height="128dp"
  android:viewportHeight="24.0"
  android:viewportWidth="24.0">
  <path
    android:fillColor="#1E9618"
    android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z" />
</vector>

If we do that we can see that the correct colouring gets applied to the generated PNG (and it will now render correctly in-app):

generated-png-fixed

Before we finish it is worth mentioning another potential gotcha. In the fixed VectorDrawable asset we’ve used a raw hex colour value: #1E9618. It may seem tempting to clean things up by using a colour resource instead:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
  android:width="128dp"
  android:height="128dp"
  android:viewportHeight="24.0"
  android:viewportWidth="24.0">
  <path
    android:fillColor="@color/colorPrimary"
    android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z" />
</vector>

Be warned: THIS DOES NOT WORK:

generated-png-resource

While colour resources are supported in Lollipop and later, they are not supported prior to that, and for the specific case we’re looking at, when auto-generating PNG assets. This issue is not quite as puzzling as the tint issue – mainly because you will actually get a lint warning if you try to use a colour resource.

Auto-generation of PNG assets is a really useful tool which makes your code-base easier to maintain because you only need to worry about vectors, and your density specific PNG assets will be generated from them. There are some gotchas along the way but, hopefully, you’ll be wiser to them now.

The source code for this article is available here.

© 2016, Mark Allison. All rights reserved.

Copyright © 2016 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

4 Comments

  1. Yeah, Vectors on Android have been pissing me off lately. It is possible to use them pre-lollipop, but it is a pain. Another issue I’ve noticed is that using vectors with an attribute like `android:drawableTop` will not work on KitKat or older versions.

    1. Perhaps you should try reading the article before commenting. The entire point is that I’m not using the compat library and therefore AppCompatImageView isn’t available.

  2. Great post!
    I’ve been having some issues when attempting to migrate some Sketch cuts to vector assets, maybe with this newfound knowledge I’ll be able to fix it 🙂

    This whole blog is a gem, the series on Constraint Layout was really good too 🙂

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.