Vector Drawables

Android 5.0 added native support for a VectorDrawable, which uses the SVG path specification to represent vector art. However, unless your minSdkVersion was 21 or higher, vector drawable resources were not that useful, as there was no good way to support the same artwork on older devices. You could somehow arrange to have PNGs for the same artwork, but then, why bother with the vector artwork in the first place?

Nowadays, vector drawable resources are more practical. Not only do more devices run Android 5.0+, but we have better tool support. Android Studio offers a Vector Asset wizard that helps you add vector drawable resources to your project, and the build system can automatically generate PNG files at various densities to be used on older devices.

As a result, vector drawables have been gaining in popularity, particularly for action bar icons.

Getting the Artwork

You have two major sources of vector drawable artwork: XML files already in the vector drawable XML format, or SVG files that you wish to convert to vector drawable XML format. Since writing the vector drawable XML by hand will be difficult at best, most vector drawable XML will start from an SVG file. Whether you do the conversion, or whether somebody else did the conversion for you, is the major difference.

For SVG that you wish to try to convert to vector drawable XML, the simpler the SVG is, the more likely it is that you will have success. In particular, SVG features like gradients and patterns are not supported. The apparent vision is for vector drawable artwork to be used mostly for things like action bar icons, where things like gradients and patterns are not necessary.

Android Studio Vector Asset Wizard

The primary way most developers will get vector drawable XML into their projects is via the Android Studio Vector Asset wizard. You can bring this up by right-clicking over the res/ directory of your desired sourceset, and choosing New > Vector Asset from the context menu:

Android Studio Vector Asset Wizard
Figure 227: Android Studio Vector Asset Wizard

The “Asset Type” radio group gives you two sources of imagery: a subset of the official Material Design icons, or your own SVG or PSD file.

By default, the Material Icon radio button is selected. You can choose which icon to display by tapping the “Icon” button, which by default shows a rendition of the Android mascot. Tapping that button brings up a poorly-organized grid of icons for you to choose from:

Android Studio Vector Asset Wizard, Material Icon Selector
Figure 228: Android Studio Vector Asset Wizard, Material Icon Selector

If you switch to the “Local file” radio button, the “Icon” button is replaced by a “Path” field, where you can pick the file that you wish to use.

By default, the Vector Asset wizard is trying to make action bar icon-sized images, 24dp square. You can override this by checking the “Override” checkbox and specifying your own size. The opacity slider allows you to indicate whether non-transparent pixels should be translucent (value from 0-99) or solid (100). If the image contains text or otherwise needs to be inverted for RTL languages, there is a checkbox to enable auto-mirroring support for that.

Also note that you can define the resource name, below where you chose the icon or SVG/PSD file. When importing an file, by default, the resource name will be the same as the base name of the file.

Clicking the “Next” button brings up a confirmation screen, where you can also change the module and sourceset if you perhaps brought up the wizard in the wrong spot:

Android Studio Vector Asset Wizard, Confirmation Screen
Figure 229: Android Studio Vector Asset Wizard, Confirmation Screen

Clicking Finish will import the resource and add it to res/drawable/ in your project. When you build your project, if your minSdkVersion is below 21, the Android Plugin for Gradle will generate PNG files to be used for those older devices. Note that these generated PNG files show up in your build/ tree, not as part of your project source code.

The preview shown in the wizard should give you an indication if your SVG is being imported properly:

Android Studio Vector Asset Wizard, Showing Failed SVG Import
Figure 230: Android Studio Vector Asset Wizard, Showing Failed SVG Import

However, even if the preview turned out OK, be sure to test your app, both on Android 5.0+ and (if relevant) Android 4.4-and-older devices, to ensure that your artwork looks the way you want it to.

Other Tools

Juraj Novák maintains a separate Android SVG to vector drawable XML converter as a Web page. If you are running into problems with the Vector Asset wizard’s import support, you might consider trying this site. It may give you better vector drawables directly, and it definitely gives you more indications about why your SVG may not convert properly.

Using the Artwork

You use vector drawable resources the same way that you use any other drawable resource. Under the covers, the Java class that handles rendering the artwork is VectorDrawable… on Android 5.0+.

If your minSdkVersion is below 21, and you want to use generated PNG files for the older devices, you need to add a line to the defaultConfig closure in your android closure in your module’s build.gradle file:


apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.1"

    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 24
        vectorDrawables.generatedDensities = ['hdpi','xxhdpi']
    }
}

Specifically, you need to set the generatedDensities property on the vectorDrawables object to an array of strings, identifying the densities for which you want PNGs generated. As with other drawable resources, devices operating on other densities than those in your chosen list will re-sample icons from one of your provided densities. If you have few vector drawables, you could list more densities and not consume much APK space. The sample shown above settles for two: hdpi for mid-range devices and xxhdpi for high-end devices.

If your minSdkVersion is 21 or higher, though, you do not need the generated PNG files, as all devices that will run your app will be capable of using the vector drawables natively.

VectorDrawableCompat

In February 2016, Google released support-vector-drawable. This contains a VectorDrawableCompat class that supports vector drawables going back to API Level 7. Google also released animated-vector-drawable, which offers AnimatedVectorDrawableCompat, supported back to API Level 11. Getting these going is tricky because they are largely undocumented and have significant limitations.

The Drawable/Vector sample project demonstrates the use of VectorDrawableCompat. In its res/drawables-nodpi/ directory, you will find a handful of vector drawable resources, culled from the Android Open Source Project. The sample app will show those in a pair of ListView widgets in tabs:

Gradle Configuration

To make VectorDrawableCompat work, you need to make some changes to your Gradle build files:

If you are using a 1.5.x version of the Android Plugin for Gradle:

Whereas if you are using 2.0.0 or higher of the Android Plugin for Gradle, you need vectorDrawables.useSupportLibrary = true in the defaultConfig closure of your android closure in your module’s build.gradle file.

These latter steps disable the automatic generation of PNG files from the vector drawable resources that would ordinarily happen by default.

The sample project uses version 1.5.0 of the Android Plugin for Gradle, and therefore it has the generatedDensities and the additionalParameters "--no-version-vectors" statements:

apply plugin: 'com.android.application'

dependencies {
    compile 'io.karim:materialtabs:2.0.2'
    compile 'com.android.support:support-v13:23.2.0'
    compile 'com.android.support:support-vector-drawable:23.2.0'
}

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 22
        generatedDensities=[]
    }

    aaptOptions {
        additionalParameters "--no-version-vectors"
    }
}

(from Drawable/Vector/app/build.gradle)

Using a newer Android Plugin for Gradle — 2.0.0 or higher – your android closure would have:


android {  
   defaultConfig {  
     vectorDrawables.useSupportLibrary = true  
    }  
}

Not generating PNG files saves us some disk space. On the other hand, now we are locked into using the vector drawable backport for these icons to be usable on older devices.

Use in Java

The sample app is cloned from another book sample, one that has a ViewPager with tabs. The activity simply sets up the ViewPager and tabs, using a SampleAdapter for the ViewPager contents. SampleAdapter, in turn, loads a VectorFragment or VectorCompatFragment into those tabs.

VectorFragment shows each of the icons in a row of a ListView, along with the resource name:

package com.commonsware.android.vector;

import android.app.ListFragment;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class VectorFragment extends ListFragment {
  private static final Integer[] VECTORS={
    R.drawable.ic_account_circle,
    R.drawable.ic_check_circle_24px,
    R.drawable.ic_corp_badge,
    R.drawable.ic_corp_icon_badge,
    R.drawable.ic_corp_statusbar_icon,
    R.drawable.ic_eject_24dp,
    R.drawable.ic_expand_more_48dp,
    R.drawable.ic_folder_24dp,
    R.drawable.ic_more_items,
    R.drawable.ic_perm_device_info,
    R.drawable.ic_sd_card_48dp,
    R.drawable.ic_settings_24dp,
    R.drawable.ic_storage_48dp,
    R.drawable.ic_usb_48dp
  };

  @Override
  public void onViewCreated(View view,
                            Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    setListAdapter(new VectorAdapter());
  }

  void applyIcon(ImageView icon, int resourceId) {
    if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP) {
      icon.setImageResource(resourceId);
    }
  }

  class VectorAdapter extends ArrayAdapter<Integer> {
    VectorAdapter() {
      super(getActivity(), R.layout.row, R.id.title, VECTORS);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      View row=super.getView(position, convertView, parent);
      ImageView icon=(ImageView)row.findViewById(R.id.icon);
      TextView title=(TextView)row.findViewById(R.id.title);

      applyIcon(icon, getItem(position));
      title.setText(getResources().getResourceName(getItem(position)));

      return(row);
    }
  }
}

(from Drawable/Vector/app/src/main/java/com/commonsware/android/vector/VectorFragment.java)

Specifically:

The VectorFragment implementation of applyIcon() simply calls setImageResource() on the ImageView, supplying the drawable resource ID. This works fine on Android 5.0 and higher, but it will fail on older devices, because older Android devices do not know natively about vector drawable resources. Hence, we only update the icon if we are on API Level 21 or higher.

So, on an Android 6.0 device, we get:

Vector Drawables, Native, Android 6.0
Figure 231: Vector Drawables, Native, Android 6.0

…while on an Android 4.4 device, we get:

Vector Drawables, Missing on Android 4.4
Figure 232: Vector Drawables, Missing on Android 4.4

VectorCompatFragment extends VectorFragment and simply overrides applyIcon():

package com.commonsware.android.vector;

import android.graphics.drawable.Drawable;
import android.support.graphics.drawable.VectorDrawableCompat;
import android.widget.ImageView;

public class VectorCompatFragment extends VectorFragment {
  @Override
  void applyIcon(ImageView icon, int resourceId) {
    Drawable d=VectorDrawableCompat.create(getResources(),
      resourceId, null);

    icon.setImageDrawable(d);
  }
}

(from Drawable/Vector/app/src/main/java/com/commonsware/android/vector/VectorCompatFragment.java)

Here, we use VectorDrawableCompat, and its static create() method, to create a Drawable to apply to the ImageView via setImageDrawable(). create() takes three parameters:

This approach works on all versions of Android supported by VectorDrawableCompat, which is API Level 7 and higher. However, on Android 5.0+ devices, create() will actually use a native vector drawable; the backport is only used on older devices.

The “Compat” tab of the Android 6.0 device and Android 4.4 device both show the icons:

Vector Drawables, Loaded via VectorDrawableCompat, Android 6.0
Figure 233: Vector Drawables, Loaded via VectorDrawableCompat, Android 6.0

Vector Drawables, Loaded via VectorDrawableCompat, Android 4.4
Figure 234: Vector Drawables, Loaded via VectorDrawableCompat, Android 4.4

Other VectorDrawable Backports

There are at least three independent backports, though, that you can try if you want to use the vector artwork directly on the older devices, rather than use PNGs generated from that vector artwork:

Note that the last one was marked as deprecated, and the others may follow at some point, given that Google now has an official backport.