I'm trying to load an XML vector drawable with the following code:
int px = Application.get().getResources()
.getDimensionPixelSize(R.dimen.bikeshare_small_marker_size);
Bitmap bitmap = Bitmap.createBitmap(px, px, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bitmap);
Drawable shape = ContextCompat.getDrawable(Application.get(), R.drawable.bike_marker_small);
shape.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
shape.draw(c);
Here is the bike_marker_small.xml
file:
<vector android:height="24dp" android:viewportHeight="210.0"
android:viewportWidth="210.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#ffffff"
android:pathData="M105.03,104.97m-53.84,0a53.84,53.84 117.34,1 1,107.67 0a53.84,53.84 117.34,1 1,-107.67 0"
android:strokeAlpha="1" android:strokeColor="#3a4677" android:strokeWidth="1.46500003"/>
<path android:fillAlpha="1" android:fillColor="#3a4677"
android:pathData="M105.1,104.67m-47.53,0a47.53,47.53 118.07,1 1,95.06 0a47.53,47.53 118.07,1 1,-95.06 0"
android:strokeAlpha="1" android:strokeColor="#000000" android:strokeWidth="0.26458332"/>
</vector>
On Android 6, 7, and 8 this works fine. However, when I try to run the app on an API 19 (Android 4.4) emulator or device, I get the following:
android.content.res.Resources$NotFoundException: File res/drawable/bike_marker_small.xml from drawable resource ID #0x7f08005f.
If the resource you are trying to use is a vector resource, you may be referencing it in an unsupported way.
See AppCompatDelegate.setCompatVectorFromResourcesEnabled() for more info.
at android.content.res.Resources.loadDrawable(Resources.java:2101)
at android.content.res.Resources.getDrawable(Resources.java:700)
at android.support.v4.content.ContextCompat.getDrawable(ContextCompat.java:353)
at org.onebusaway.android.map.googlemapsv2.bike.BikeStationOverlay.createBitmapFromShape(BikeStationOverlay.java:195)
at org.onebusaway.android.map.googlemapsv2.bike.BikeStationOverlay.(BikeStationOverlay.java:87)
at org.onebusaway.android.map.googlemapsv2.BaseMapFragment.setupBikeStationOverlay(BaseMapFragment.java:474)
I have the following in my build.gradle
:
android {
compileSdkVersion 27
buildToolsVersion "26.0.2"
defaultConfig {
minSdkVersion 14
targetSdkVersion 21
versionCode 86
versionName "2.3.1"
vectorDrawables.useSupportLibrary = true
}
...
Why isn't this working?
Trying creating the drawable shape
using VectorDrawableCompat.create()
instead of ContextCompat.getDrawable()
:
Drawable shape = VectorDrawableCompat.create(
Application.get().getResources(),
R.drawable.bike_marker_small,
Application.get().getTheme()
);
Answer from Sean Barbeau is defintely great, although I'd like to add something that happened to me and that could cause troubles to other people trying to figure out what's going on with VectorDrawable
on API 19 or API 20.
I created a method to obtain the VectorDrawable
as Drawable
for cases when I don't need/want to use them as Drawable
, but as Bitmap
, for example. This method would work for Android versions >= API 19.
This is the Kotlin method (look at the end for the improved version):
fun getDrawable(context:Context, drawableId: Int): Drawable {
var drawable: Drawable?
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
drawable = VectorDrawableCompat.create(context.resources, drawableId, context.theme)
} else {
drawable = ContextCompat.getDrawable(context, drawableId)
}
return drawable
}
Basically, I pass the context (since this method would be a helper method and will be in a class without a Context
or an Activity
) and the resource ID of the VectorDrawable
.
It could happen, that you directly call this helper method with the resource ID using the R namespace, like this:
ImageUtils.Companion.getDrawable(
getBaseContext(),
R.drawable.your_vector_drawable
)
However, it could be as well that you generate the resource ID dynamically, similar to:
@DrawableRes val resourceID = context.resources.getIdentifier(
"resource_prefix_" + type,
"drawable",
context.packageName
)
And then call to the helper method, in the same way as before, using the resourceID variable:
ImageUtils.Companion.getDrawable(
getBaseContext(),
resourceID
)
If you do it this way, while on API < 21, the dynamically generated resource ID would most likely be a PNG. And the helper method will fail, because it expected a XML (SVG). You can fix it by requesting a normal Drawable
(and not a VectorDrawable
), by catching the NotFoundException
that will be raised and just getting a Drawable
. This is the final helper method:
fun getDrawable(context:Context, drawableId: Int): Drawable {
var drawable: Drawable?
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
try {
drawable = VectorDrawableCompat.create(context.resources, drawableId, context.theme)
} catch (ex: Resources.NotFoundException) {
drawable = ContextCompat.getDrawable(context, drawableId)
}
} else {
drawable = ContextCompat.getDrawable(context, drawableId)
}
return drawable
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With