How to use vector drawables with View besides ImageView with srcCompat?
Asked Answered
H

9

41

app:srcCompat with ImageView allows for backward compatible use of vector drawables. But how can you use them with other Views besides ImageView? For example, the TextView attributes like android:drawableLeft.

Also using the vector drawable as an android:icon with MenuItem caused a crash with the following exception:

Fatal Exception: android.view.InflateException: Binary XML file line #2: Error inflating class <unknown>
   at android.view.LayoutInflater.createView(LayoutInflater.java:626)
   at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:702)
   at android.view.LayoutInflater.inflate(LayoutInflater.java:470)
   at android.view.LayoutInflater.inflate(LayoutInflater.java:398)
   at android.support.v7.view.menu.MenuItemImpl.setActionView(MenuItemImpl.java:621)
   at android.support.v7.view.menu.MenuItemImpl.setActionView(MenuItemImpl.java:40)
   at android.support.v4.view.MenuItemCompat.setActionView(MenuItemCompat.java:310)
   at android.support.v7.view.SupportMenuInflater$MenuState.setItem(SupportMenuInflater.java:465)
   at android.support.v7.view.SupportMenuInflater$MenuState.addItem(SupportMenuInflater.java:479)
   at android.support.v7.view.SupportMenuInflater.parseMenu(SupportMenuInflater.java:196)
   at android.support.v7.view.SupportMenuInflater.inflate(SupportMenuInflater.java:118)
   at com.example.niceapp.context.main.MainActivity.onCreateOptionsMenu(MainActivity.java:101)
   at android.app.Activity.onCreatePanelMenu(Activity.java:2578)

With Support Library 23.2.0, how can this issue be addressed?

Halothane answered 4/3, 2016 at 15:53 Comment(0)
L
31

For AppCompat version 23.3.0 where no work solution via selector XML (razzledazzle's accepted answer) we can do this by programmatically:

activity_main.xml

<android.support.v7.widget.AppCompatImageButton
    android:id="@+id/btnEnter"
    />

MainActivity.java

AppCompatImageButton image = (AppCompatImageButton) findViewById(R.id.btnEnter);
if (image != null) {
    VectorDrawableCompat vcAccept = VectorDrawableCompat.create(getResources(), R.drawable.vc_accept, getTheme());
    VectorDrawableCompat vcAcceptWhite = VectorDrawableCompat.create(getResources(), R.drawable.vc_accept_white, getTheme());

    StateListDrawable stateList = new StateListDrawable();
    stateList.addState(new int[]{android.R.attr.state_focused, -android.R.attr.state_pressed}, vcAccept);
    stateList.addState(new int[]{android.R.attr.state_focused, android.R.attr.state_pressed}, vcAcceptWhite);
    stateList.addState(new int[]{-android.R.attr.state_focused, android.R.attr.state_pressed}, vcAcceptWhite);
    stateList.addState(new int[]{}, vcAccept);

    image.setImageDrawable(stateList);
}

This code is equivalent for this selector xml:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/vc_accept" />
    <item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/vc_accept_white" />
    <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/vc_accept_white" />
    <item android:drawable="@drawable/vc_accept" />
</selector>

UPDATE

If the vector drawable is not shown using API 23, you'll need to convert the VectorDrawable to a regular Drawable first. If you want to use setCompoundDrawablesWithIntrinsicBounds you'll need to do this, but for StateListDrawable I didn't need to.

Drawable icon;
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
    icon = VectorDrawableCompat.create(getResources(), R.drawable.vc_icon, getContext().getTheme());
} else {
    icon = getResources().getDrawable(R.drawable.vc_icon, getContext().getTheme());
}
Luge answered 21/4, 2016 at 7:33 Comment(1)
Thanks!!! Setting the vector drawable with setImageResource did not work in my Adapter. Creating a VectorDrawableCompat of it and setting it as ImageDrawable worked perfectly!Finicking
H
45

Update 2: They have added an option to enable it again in Support Library 23.4.0:

For AppCompat users, we’ve added an opt-in API to re-enable support Vector Drawables from resources (the behavior found in 23.2) via AppCompatDelegate.setCompatVectorFromResourcesEnabled() - keep in mind that this still can cause issues with memory usage and problems updating Configuration instances, hence why it is disabled by default.

Update: This no longer works starting from version 23.3.0

For AppCompat users, we’ve decided to remove the functionality which let you use vector drawables from resources on pre-Lollipop devices due to issues found in the implementation in version 23.2.0/23.2.1 [https://code.google.com/p/android/issues/detail?id=205236, https://code.google.com/p/android/issues/detail?id=204708]. Using app:srcCompat and setImageResource() continues to work.

From Android Developers Google+ post


Using AppCompat and app:srcCompat is the most foolproof method of integrating vector drawables into your app.

That quote is from the official blogpost for the release of version 23.2.0 of the Support Library.

The post also mentions the following:

You’ll find directly referencing vector drawables outside of app:srcCompat will fail prior to Lollipop. However, AppCompat does support loading vector drawables when they are referenced in another drawable container such as a StateListDrawable, InsetDrawable, LayerDrawable, LevelListDrawable, and RotateDrawable. By using this indirection, you can use vector drawables in cases such as TextView’s android:drawableLeft attribute, which wouldn’t normally be able to support vector drawables.

This translates to the steps below:

Step 1:

Create or import a vector resource which you require for the app. For example, one can create a vector drawable for the search icon and name it ic_action_search_vector.xml

Step 2:

Create another proxy drawable resource for the vector drawable previously created. Say, for the previous ic_action_search_vector.xml, ic_action_search.xml can be created as a simple StateListDrawable which could contain the lines below:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_action_search_vector"/>
</selector>

This step can be skipped if you have referenced the vector drawable from another drawable resource which you will use with your View.

Step 3:

Use the drawable resource (here, ic_action_search.xml) that refers to the vector drawable (ic_action_search_vector.xml) instead of the vector drawable directly. For a menu, it would look like:

<item android:id="@+id/search"
        android:title="@string/search"
        android:icon="@drawable/ic_action_search"
        app:showAsAction="always"/>

This is the solution to that problem!

Halothane answered 4/3, 2016 at 15:53 Comment(1)
Hi, we can do this by programmatically for AppCompat version 23.3.0, see my answer please.Lineolate
L
31

For AppCompat version 23.3.0 where no work solution via selector XML (razzledazzle's accepted answer) we can do this by programmatically:

activity_main.xml

<android.support.v7.widget.AppCompatImageButton
    android:id="@+id/btnEnter"
    />

MainActivity.java

AppCompatImageButton image = (AppCompatImageButton) findViewById(R.id.btnEnter);
if (image != null) {
    VectorDrawableCompat vcAccept = VectorDrawableCompat.create(getResources(), R.drawable.vc_accept, getTheme());
    VectorDrawableCompat vcAcceptWhite = VectorDrawableCompat.create(getResources(), R.drawable.vc_accept_white, getTheme());

    StateListDrawable stateList = new StateListDrawable();
    stateList.addState(new int[]{android.R.attr.state_focused, -android.R.attr.state_pressed}, vcAccept);
    stateList.addState(new int[]{android.R.attr.state_focused, android.R.attr.state_pressed}, vcAcceptWhite);
    stateList.addState(new int[]{-android.R.attr.state_focused, android.R.attr.state_pressed}, vcAcceptWhite);
    stateList.addState(new int[]{}, vcAccept);

    image.setImageDrawable(stateList);
}

This code is equivalent for this selector xml:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/vc_accept" />
    <item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/vc_accept_white" />
    <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/vc_accept_white" />
    <item android:drawable="@drawable/vc_accept" />
</selector>

UPDATE

If the vector drawable is not shown using API 23, you'll need to convert the VectorDrawable to a regular Drawable first. If you want to use setCompoundDrawablesWithIntrinsicBounds you'll need to do this, but for StateListDrawable I didn't need to.

Drawable icon;
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
    icon = VectorDrawableCompat.create(getResources(), R.drawable.vc_icon, getContext().getTheme());
} else {
    icon = getResources().getDrawable(R.drawable.vc_icon, getContext().getTheme());
}
Luge answered 21/4, 2016 at 7:33 Comment(1)
Thanks!!! Setting the vector drawable with setImageResource did not work in my Adapter. Creating a VectorDrawableCompat of it and setting it as ImageDrawable worked perfectly!Finicking
H
18

Vector drawables can be used pre-Lollipop in other places than app:srcCompat, but it comes with a price.

I've made this diagram to help (valid for Support Library 23.4.0 to - at least - 25.1.0).

VectorDrawable cheatsheet

Holbein answered 25/1, 2017 at 17:16 Comment(0)
I
6

You can add Vector Drawable in TextView programmatically. Make use of VectorDrawableCompat inorder to add drawableLeft/ drawableRight /drawableTop/ drawableBottom/ drawableStart/ drawableEnd.

Steps:

i. If TextView is inside Activity:

TextView tvUserName= (TextView)findViewById(R.id.et_username_or_email);
VectorDrawableCompat drawableCompat=VectorDrawableCompat.create(getResources(), R.drawable.layer_list_ic_user, tvUserName.getContext().getTheme());
tvUserName.setCompoundDrawablesRelativeWithIntrinsicBounds(drawableCompat, null, null, null);

ii. If TextView is inside Fragment:

TextView tvUserName= (TextView )view.findViewById(R.id.et_username_or_email);
VectorDrawableCompat drawableCompat=VectorDrawableCompat.create(getActivity().getResources(), R.drawable.layer_list_ic_user, tvUserName.getContext().getTheme());
tvUserName.setCompoundDrawablesRelativeWithIntrinsicBounds(drawableCompat, null, null, null);

For more information on VectorDrawableCompat Refer this link

Imagination answered 29/11, 2016 at 10:39 Comment(5)
this is working, thank you but theme is compulsory to add?Statampere
@RuchaBhatt Yes, it's compulsory.Imagination
yes, i checked it, but u can pass null so it's fine. :)Statampere
@RuchaBhatt Happy to help & if you are using custom text view then it's good practice to use theme.Imagination
Thanks but this solution requires API 17+ (Jelly Bean MR1)Hukill
N
1

Android 5.0 (API level 21) and higher provides vector drawable support. If your app has a minimum API level that is lower, Vector Asset Studio adds the vector drawable file to your project; also, at build time, Gradle creates PNG raster images at various resolutions. Gradle generates the PNG densities specified by the Domain Specific Language (DSL) generatedDensities property in a build.gradle file. To generate PNGs, the build system requires Android Plugin for Gradle 1.5.0 or higher.

This is not true if you include in your gradle vectorDrawables.useSupportLibrary = true

either set to false or remove the line completely and all your vectors will work as they were. But for olders versions of android they will be able to rely on the converted PNG

Nether answered 7/9, 2016 at 14:49 Comment(0)
F
1

Form android studio 3.0.0 android:src is not support vector image and below 21 my get exception. use app:srcCompat for vector image. Keep all vector image files inside the drawable folder.

android {  
   defaultConfig {  
     vectorDrawables.useSupportLibrary = true  
    }  
 }

And in application class define this:

@Override
public void onCreate() {
    super.onCreate();
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

Now you can use your .xml file. Don't forget to user this link: xmlns:app="http://schemas.android.com/apk/res-auto"

<RelativeLayout
    android:id="@+id/counterValuePanel"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/ic_cart_notify"/>
</RelativeLayout>

Now you can use app:srcCompat="@drawable/ic_cart_notify" but if you try to use in android:background or android:drawableLeft then you got "Error inflating" exception. For that create a new wrapped drawable .xml file, ic_cart_notify is vector icon.

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_cart_notify"/>
</layer-list>
Floppy answered 31/12, 2017 at 4:54 Comment(0)
C
0

Dadou is right. So if you would like use selector for view with VectorDrawables you need to add:

    static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(trfor);
}

to every Activity where you want to use VectorDrawables on devices with versions below Android 5.

Catling answered 9/1, 2018 at 12:24 Comment(0)
R
0

set vector drawable to side drawable in textview in android

AppCompatTextView now supports app:drawableLeftCompat, app:drawableTopCompat, app:drawableRightCompat, app:drawableBottomCompat, app:drawableStartCompat and app:drawableEndCompat compound drawables, supporting backported drawable types such as VectorDrawableCompat.

Include this in your gradle file

implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'

In your text view you can use

app:drawableLeftCompat
app:drawableStartCompat
Rovelli answered 15/1, 2019 at 8:56 Comment(0)
M
-1

I'm using new support library and all I have to do is:

compile 'com.android.support:appcompat-v7:25.1.1'

In Build.gradle file

 defaultConfig {

    vectorDrawables.useSupportLibrary = true
}

Now Where ever you are using like fragment, activity or adapter use this as the first line in your class

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

after that use like as we do before, something.xml

<ImageView
    android:id="@+id/ivMainNavigationIcon"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

Something.java

thumbIcon.setImageDrawable(ContextCompat.getDrawable(context,R.drawable.ic_check_circle_black_24dp));

or if you have drawable id needed to be set dynamically

thumbIcon.setImageDrawable(ContextCompat.getDrawable(context,drawableID));
Mammy answered 16/2, 2017 at 18:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.