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.
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.
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:
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:
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:
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:
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.
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.
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.
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:
VectorDrawableCompat
and will work for all API levels
that the project supports (15 and higher, based on the project’s
minSdkVersion
).To make VectorDrawableCompat
work, you need to make some changes
to your Gradle build files:
build.gradle
filecom.android.support:support-vector-drawable
in your module’s build.gradle
file, akin to your other Android Support
dependenciesIf you are using a 1.5.x version of the Android Plugin for Gradle:
generatedDensities
in your defaultConfig
closure,
inside your android
closure in your module’s build.gradle
fileadditionalParameters "--no-version-vectors"
in an
aaptOptions
closure inside your android
closure
in your module’s build.gradle
fileWhereas 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"
}
}
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.
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);
}
}
}
Specifically:
VECTORS
VectorFragment
uses a VectorAdapter
to populate the ListView
VectorAdapter
, in getView()
, uses getResourceName()
on a
Resources
object to get the resource name associated with a resource
ID, to show in a TextView
in the rowVectorAdapter
delegates to VectorFragment
and its applyIcon()
method to populate the ImageView
given a drawable resource IDThe 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:
Figure 231: Vector Drawables, Native, Android 6.0
…while on an Android 4.4 device, we get:
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);
}
}
Here, we use VectorDrawableCompat
, and its static
create()
method,
to create a Drawable
to apply to the ImageView
via setImageDrawable()
.
create()
takes three parameters:
Resources
objectnull
to use the app’s default themeThis 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:
Figure 233: Vector Drawables, Loaded via VectorDrawableCompat, Android 6.0
Figure 234: Vector Drawables, Loaded via VectorDrawableCompat, Android 4.4
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.