Get Drawable Of Vector Resource (with support library backward compatibility)

April 6, 2018
Vector Drawables Backward Compatibility support back to Android 2.1 (API level 7+).

Vector Resources is not supported for < API level 21, thus the Vector Drawables Backward Compatibility is required.

Module:app build.gradle

Edit Module:app build.gradle

  • include vectorDrawables.useSupportLibrary = true
  • include com.android.support:appcompat-v7 or com.android.support:design
  • include com.android.support:support-vector-drawable
...

android {
  defaultConfig {
    applicationId ...
    ...
    vectorDrawables.useSupportLibrary = true
  }
}

dependencies {
    // https://developer.android.com/topic/libraries/support-library/revisions.html
    // implementation "com.android.support:support-v4:26.1.0"
    // implementation "com.android.support:appcompat-v7:26.1.0"
    implementation "com.android.support:support-vector-drawable:26.1.0"
    implementation "com.android.support:design:26.1.0"
}
...

AppCompatDelegate.setCompatVectorFromResourcesEnabled

It’s recommended to call AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) at Application.onCreate.

Sets whether vector drawables on older platforms (< API 21) can be used within DrawableContainer resources.

When enabled, AppCompat can intercept some drawable inflation from the framework, which enables implicit inflation of vector drawables within DrawableContainer resources. You can then use those drawables in places such as android:src on ImageView, or android:drawableLeft on TextView.

This feature defaults to disabled, since enabling it can cause issues with memory usage, and problems updating Configuration instances. If you update the configuration manually, then you probably do not want to enable this. You have been warned.

Even with this disabled, you can still use vector resources through setImageResource(int) and its app:srcCompat attribute. They can also be used in anything which AppCompat inflates for you, such as menu resources.

Please note: this only takes effect in Activities created after this call.

If you don’t do this, you might bump into android.content.res.Resources$NotFoundException: File res/drawable/ic_error_black_24dp.xml from drawable resource ID #0x7f08006a. 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 on older platforms (< API 21).

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    }
}

NOTE: remember to load your application class in AndroidManifest.xml -> application.android:name.

Load vector drawable

Get vector Drawable programatically

val icon = AppCompatResources.getDrawable(context, R.drawable.ic_error_black_24dp)
val icon = ContextCompat.getDrawable(context, R.drawable.ic_error_black_24dp)

NOTE: AppCompatResources.getDrawable - This method supports inflation of and resources on devices where platform support is not available.

NOTE: Seems like AppCompatResources.getDrawable is more realiable for vector, but ContextCompat.getDrawable works when AppCompatDelegate.setCompatVectorFromResourcesEnabled is used.

imageView.setImageResource(context, R.drawable.ic_error_black_24dp)

NOTE: ImageView.setImageResource is equivelant to app:srcCompat in layout xml.

Load vector Drawable using app:srcCompat in layout.

<ImageView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  app:srcCompat="@drawable/ic_add_black_24dp" />
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:tint="@color/fab_tint"
        app:srcCompat="@drawable/ic_add_black_24dp" />

</android.support.design.widget.CoordinatorLayout>

Get vector Drawable from TypedArray for custom view

class BadgePreference : Preference {
    private var badge: Drawable? = null

    constructor(context: Context) : super(context) {}

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        val a = context.theme.obtainStyledAttributes(
                attrs,
                R.styleable.PreferencesBadge,
                0, 0)

        try {
            // Resources$NotFoundException
            // badge = a.getDrawable(R.styleable.PreferencesBadge_badge)
            val drawableResId = a.getResourceId(R.styleable.PreferencesBadge_badge, -1);
            badge = AppCompatResources.getDrawable(context, drawableResId)
        }
        finally {
            a.recycle()
        }
    }

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
    }
}

Content of res/values/attrs.xml

<resources>
    <declare-styleable name="PreferencesBadge">
        <attr name="badge" format="reference" />
    </declare-styleable>
</resources>

Specify custom drawable attribute in layout.

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res/com.mypackage.myapp">

    <com.mypackage.myapp.BadgePreference
        android:key="@string/pref_key_export_text"
        android:title="Export as Text"
        android:summary="Export as data as plain text file. Keep the file safe as it is not encrypted"
        custom:badge="@drawable/ic_upcoming"
        />

</PreferenceScreen>

References:

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.