Vector Drawable in Layer List on Older Android Versions

Semdog picture Semdog · Mar 19, 2017 · Viewed 15.8k times · Source

On newer Android versions, the following code:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
            <shape android:shape="oval">
                <solid android:color="#bdbdbd" />
                <size
                    android:width="60dp"
                    android:height="60dp" />
            </shape>
    </item>
    <item
        android:drawable="@drawable/ic_library_books_black_24dp"
        android:gravity="center"
        android:width="40dp"
        android:height="40dp"
        >
    </item>
</layer-list>

produces this flawlessly:

Vector drawable showing white lists on grey circular background

However, earlier Android versions (API 16 and 19, from what I've tested) do not like this at all and I get

E/AndroidRuntime: FATAL EXCEPTION: main
              Process: package.app, PID: 11490
              android.view.InflateException: Binary XML file line #26: Error inflating class ImageView

upon inflation. I have used app:srcCompat for all my ImageViews so there is no problem there.

Standard Vector Drawables also work fine, but when placed in a layer-list they cause mayhem. Are there any workarounds?

Answer

Rapunzel Van Winkle picture Rapunzel Van Winkle · Apr 25, 2017

The width/height attributes for the vector drawable in your layer-list are only supported in API 23 (Marshmallow) and higher. If you look at your layer-list drawable in the Android Studio editor, these attributes should have yellow blocks around them along with a warning that this won't work reliably on older devices.

But I think you can get rid of the warning and achieve the same centering effect like this:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="oval">
            <solid android:color="#bdbdbd" />
            <size
                android:width="60dp"
                android:height="60dp" />
        </shape>
    </item>
    <item
        android:drawable="@drawable/ic_library_books_black_24dp"
        android:top="10dp"
        android:bottom="10dp"
        android:left="10dp"
        android:right="10dp">
    </item>
</layer-list>

I tested this on an old API 15 phone and it worked fine. I hope this works for you too.

Update:

In a previous version of this answer, I'd advised against using vectorDrawables.useSupportLibrary = true with layer lists, because it caused crashes. However, I've recently learned about a workaround that seems to fix the crash (while avoiding the fat auto-generated png files that @android developer correctly mentioned). Here's a summary of what needs to be done for it to work correctly:

Be sure to use srcCompat in your xml.

    <android.support.v7.widget.AppCompatImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@drawable/layer_list_with_svg" />

Add 'vectorDrawables.useSupportLibrary' to app/build.gradle

 android {
   defaultConfig {
     vectorDrawables.useSupportLibrary = true
   }
 }

Add 'setCompatVectorFromResourcesEnabled(true)' to onCreate

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    setContentView(R.layout.activity_main);
}

Why is all of this necessary?

As a more knowledgable person explained it to me, this has to do with how Android loads up the compat vector drawables. If you're using vectorDrawables.useSupportLibrary = true then image views will load up VectorDrawableCompat drawables when you use app:srcCompat. When your layer list drawable is inflated, there's no way for it to figure out how to create those referenced vector drawables. But if you turn on setCompatVectorFromResourcesEnabled it will try to hook the vector drawable loading in at a much lower level than image views and their app:srcCompat attribute, so it's then able to figure out how to load the vector drawables referenced in the layer list.