131

When I use VectorDrawable assets in a textview or imageview I get a runtime crash when using "android:DrawableRight" / "android:DrawableEnd" / "android:DrawableStart" / "android:DrawableLeft".

The app will compile fine without any warnings.

I am using

  • Gradle 1.5
  • Support Library 23.2 ('com.android.support:appcompat-v7:23.2.0')

What I have found though is that I can programmatically assign SVG's in Java without crashes like this.

TextView tv = (TextView) findViewById(R.id.textView);
tv.setCompoundDrawablesWithIntrinsicBounds(null,null, getResources().getDrawable(R.drawable.ic_accessible_white_36px),null);

(I suspect this is a support library bug for 23.2.)

But is it possible to use drawableRight etc for SVG assets ?

Here is my layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="au.com.angryitguy.testsvg.MainActivity">


<TextView
    android:id="@+id/textView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:drawableRight="@drawable/ic_accessible_white_36px"
    android:background="@color/colorPrimary"
    android:textColor="#FFFFFF"
    android:textSize="22sp"
    android:text="Hello World!"/>
</RelativeLayout>

Here is my Activity

package au.com.angryitguy.testsvg;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        }
    }

Here is the unmodified VectorDrawable asset from Google's material design site.

<vector android:height="24dp" android:viewportHeight="24.0"
    android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="#FFFFFF" android:pathData="M12,4m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
    <path android:fillColor="#FFFFFF" android:pathData="M19,13v-2c-1.54,0.02 -3.09,-0.75 -4.07,-1.83l-1.29,-1.43c-0.17,-0.19 -0.38,-0.34 -0.61,-0.45 -0.01,0 -0.01,-0.01 -0.02,-0.01L13,7.28c-0.35,-0.2 -0.75,-0.3 -1.19,-0.26C10.76,7.11 10,8.04 10,9.09L10,15c0,1.1 0.9,2 2,2h5v5h2v-5.5c0,-1.1 -0.9,-2 -2,-2h-3v-3.45c1.29,1.07 3.25,1.94 5,1.95zM12.83,18c-0.41,1.16 -1.52,2 -2.83,2 -1.66,0 -3,-1.34 -3,-3 0,-1.31 0.84,-2.41 2,-2.83L9,12.1c-2.28,0.46 -4,2.48 -4,4.9 0,2.76 2.24,5 5,5 2.42,0 4.44,-1.72 4.9,-4h-2.07z"/>
</vector>

Here is my app build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "au.com.angryitguy.testsvg"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        // Stops the Gradle plugin’s automatic rasterization of vectors
        generatedDensities = []
    }
    // Flag to tell aapt to keep the attribute ids around
    aaptOptions {
        additionalParameters "--no-version-vectors"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.2.0'
}

Here is the crash. (Note the inflate errors that reference the textview.)

java.lang.RuntimeException: Unable to start activity ComponentInfo{
    au.com.angryitguy.testsvg/au.com.angryitguy.testsvg.MainActivity}: 
    android.view.InflateException: Binary XML file line #13: 
    Error inflating class TextView

at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2059)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
at android.app.ActivityThread.access$600(ActivityThread.java:130)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
...

Caused by: android.view.InflateException: 
    Binary XML file line #13: Error inflating class TextView
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:267)
at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:129)
at au.com.angryitguy.testsvg.MainActivity.onCreate(MainActivity.java:14)
at android.app.Activity.performCreate(Activity.java:5008)
...

Caused by: android.content.res.Resources$NotFoundException: 
    File res/drawable/ic_accessible_white_36px.xml from drawable resource ID #0x7f02004b
at android.content.res.Resources.loadDrawable(Resources.java:1918)
at android.content.res.TypedArray.getDrawable(TypedArray.java:601)
at android.widget.TextView.<init>(TextView.java:622)
at android.support.v7.widget.AppCompatTextView.<init>(AppCompatTextView.java:60)
at android.support.v7.widget.AppCompatTextView.<init>(AppCompatTextView.java:56)
at android.support.v7.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:103)
at android.support.v7.app.AppCompatDelegateImplV7.createView(AppCompatDelegateImplV7.java:963)
at android.support.v7.app.AppCompatDelegateImplV7.onCreateView(AppCompatDelegateImplV7.java:1022)
at android.support.v4.view.LayoutInflaterCompatHC$FactoryWrapperHC.onCreateView(LayoutInflaterCompatHC.java:44)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:675)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:746) 
at android.view.LayoutInflater.inflate(LayoutInflater.java:489) 
at android.view.LayoutInflater.inflate(LayoutInflater.java:396) 
at android.view.LayoutInflater.inflate(LayoutInflater.java:352) 
at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:267) 
at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:129) 
at au.com.angryitguy.testsvg.MainActivity.onCreate(MainActivity.java:14) 
at android.app.Activity.performCreate(Activity.java:5008) 
...

Caused by: org.xmlpull.v1.XmlPullParserException:
    Binary XML file line #1: invalid drawable tag vector
at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java:877)
at android.graphics.drawable.Drawable.createFromXml(Drawable.java:818)
at android.content.res.Resources.loadDrawable(Resources.java:1915)
at android.content.res.TypedArray.getDrawable(TypedArray.java:601) 
at android.widget.TextView.<init>(TextView.java:622) 
at android.support.v7.widget.AppCompatTextView.<init>(AppCompatTextView.java:60) 
at android.support.v7.widget.AppCompatTextView.<init>(AppCompatTextView.java:56) 
at android.support.v7.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:103) 
at android.support.v7.app.AppCompatDelegateImplV7.createView(AppCompatDelegateImplV7.java:963) 
at android.support.v7.app.AppCompatDelegateImplV7.onCreateView(AppCompatDelegateImplV7.java:1022) 
at android.support.v4.view.LayoutInflaterCompatHC$FactoryWrapperHC.onCreateView(LayoutInflaterCompatHC.java:44) 
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:675) 
at android.view.LayoutInflater.rInflate(LayoutInflater.java:746) 
at android.view.LayoutInflater.inflate(LayoutInflater.java:489) 
at android.view.LayoutInflater.inflate(LayoutInflater.java:396) 
at android.view.LayoutInflater.inflate(LayoutInflater.java:352) 
at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:267) 
at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:129) 
at au.com.angryitguy.testsvg.MainActivity.onCreate(MainActivity.java:14) 
at android.app.Activity.performCreate(Activity.java:5008) 
...
JJD
  • 50,076
  • 60
  • 203
  • 339
angryITguy
  • 9,332
  • 8
  • 54
  • 82
  • No, you can't, since SVG files *aren't supported natively*. You have to use a VectorDrawable, instead (which only uses a subset of the SVG specs). – Phantômaxx Mar 03 '16 at 09:17
  • 2
    To see how use VectorDrawable with drawableLeft, drawableRight, drawableTop, drawableBottom check out [This answer](http://stackoverflow.com/a/40250753/5079879) – Behzad Bahmanyar Oct 25 '16 at 22:34
  • I found this works for me : https://android.jlelse.eu/android-vector-drawables-on-pre-lollipop-crash-solution-45c0c34f0160 – Huy Tower Feb 15 '19 at 07:37

17 Answers17

207

it possible to use drawableRight etc for SVG assets ?

Yes

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

If you have problems while using app:drawableLeftCompat, app:drawableStartCompat in buttons you will need to update your library to

androidx.appcompat:appcompat:1.2.0-alpha01

they had a bug on

androidx.appcompat:appcompat:1.1.0-alpha01

you can see the docs


Or if you don't want to update yet, then:

Because it seems Google not going to do anything about this issue any time soon, I had to came up with a more solid reusable solution for all of my apps:

  1. First add custom TextView attributes in attrs.xml file of your app "res/values/attrs.xml" :

    <resources>
        <declare-styleable name="CustomTextView">
            <attr name="drawableStartCompat" format="reference"/>
            <attr name="drawableEndCompat" format="reference"/>
            <attr name="drawableTopCompat" format="reference"/>
            <attr name="drawableBottomCompat" format="reference"/>
        </declare-styleable>
    </resources>
    
  2. Then create custom TextView class like this:

    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.drawable.Drawable;
    import android.os.Build;
    import android.support.v7.content.res.AppCompatResources;
    import android.support.v7.widget.AppCompatTextView;
    import android.util.AttributeSet;
    
    public class CustomTextView extends AppCompatTextView {
        public CustomTextView(Context context) {
            super(context);
        }    
        public CustomTextView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initAttrs(context, attrs);
        }
        public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initAttrs(context, attrs);
        }
    
        void initAttrs(Context context, AttributeSet attrs) {
            if (attrs != null) {
                TypedArray attributeArray = context.obtainStyledAttributes(
                        attrs,
                        R.styleable.CustomTextView);
    
                Drawable drawableStart = null;
                Drawable drawableEnd = null;
                Drawable drawableBottom = null;
                Drawable drawableTop = null;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    drawableStart = attributeArray.getDrawable(R.styleable.CustomTextView_drawableStartCompat);
                    drawableEnd = attributeArray.getDrawable(R.styleable.CustomTextView_drawableEndCompat);
                    drawableBottom = attributeArray.getDrawable(R.styleable.CustomTextView_drawableBottomCompat);
                    drawableTop = attributeArray.getDrawable(R.styleable.CustomTextView_drawableTopCompat);
                } else {
                    final int drawableStartId = attributeArray.getResourceId(R.styleable.CustomTextView_drawableStartCompat, -1);
                    final int drawableEndId = attributeArray.getResourceId(R.styleable.CustomTextView_drawableEndCompat, -1);
                    final int drawableBottomId = attributeArray.getResourceId(R.styleable.CustomTextView_drawableBottomCompat, -1);
                    final int drawableTopId = attributeArray.getResourceId(R.styleable.CustomTextView_drawableTopCompat, -1);
    
                    if (drawableStartId != -1)
                        drawableStart = AppCompatResources.getDrawable(context, drawableStartId);
                    if (drawableEndId != -1)
                        drawableEnd = AppCompatResources.getDrawable(context, drawableEndId);
                    if (drawableBottomId != -1)
                        drawableBottom = AppCompatResources.getDrawable(context, drawableBottomId);
                    if (drawableTopId != -1)
                        drawableTop = AppCompatResources.getDrawable(context, drawableTopId);
                }
    
                // to support rtl
                setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStart, drawableTop, drawableEnd, drawableBottom);
                attributeArray.recycle();
            }
        }
    }
    
  3. Now you can use it easily in any layouts by your custom attributes:

    <YOUR_VIEW_PACKAGE.CustomTextView
        android:id="@+id/edt_my_edit_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:drawableStartCompat="@drawable/your_vector_drawable" <!-- vector drawable -->
        app:drawableEndCompat="@drawable/your_vector_drawable" <!-- vector drawable -->
        app:drawableTopCompat="@drawable/your_vector_drawable" <!-- vector drawable -->
        app:drawableBottomCompat="@drawable/your_vector_drawable" <!-- vector drawable -->
        />
    
  • You can do similar thing with Button, EditText and RadioButton because they derived from TextView
starball
  • 20,030
  • 7
  • 43
  • 238
Behzad Bahmanyar
  • 6,195
  • 4
  • 35
  • 41
  • 4
    Very useful answer. Anyway, I recommend reading [Dadou's very complete answer](https://stackoverflow.com/a/41855991/370798) too. Removing `vectorDrawables { useSupportLibrary = true }` from my `build.gradle` as that answer suggests worked for me. – Sam Aug 03 '17 at 00:45
  • I did that and I have now the following problem: when I attach the onclick in XML I get the following error: java.lang.NoSuchMethodException: onClick [class android.view.View] – Ramdane Oualitsen Sep 20 '17 at 04:11
  • 4
    I disagree with the removal of the `vectorDrawables useSupportLibrary = true` line from the gradle. When you remove it, you can still put vectors inside your views, but they resize the same way as pngs, which means they will be stretched and become grainy. If you want devices below 5.0/API21 to actually resize the vectors correctly so they look crisp, you must use that line inside the gradle file. Putting that line in invokes the IDE to find areas where you are using vectors incorrectly and then you must use the `app:srcCompat` via XML or set it via code with `VectorDrawableCompat.create()` – Heinous Games Nov 08 '17 at 17:46
  • How to add `app:drawableEndCompat` for better RTL support? Cause `setCompoundDrawablesRelativeWithIntrinsicBounds` needs API level 17 at least. – Dr.jacky Jan 07 '18 at 08:49
  • The manual width and height in XML, doesn't work. You have to add a custom attribute for `width/height`. – Dr.jacky Jan 15 '18 at 08:56
  • Thanks. Don't forget to wrap TypedArray settings in try-finally block. Also -1 can be replaced with 0. – CoolMind Feb 14 '18 at 15:30
  • it still doesn't support for AppCompatButton :( – Arsenius Mar 11 '19 at 09:51
  • 1
    What about setting the drawable programmatically? – android developer Mar 17 '19 at 09:33
  • The link as of july 2019 does not contain information of supporting vector, here is the link that shows that. https://developer.android.com/jetpack/androidx/releases/appcompat#1.1.0-alpha01 – hjchin Jul 06 '19 at 06:00
  • For all the people who has problems with using drawableLeftCompat/drawableRightCompat in Buttons you need to update your dependencies to androidx.appcompat:appcompat:1.2.0-alpha01 – DemoDemo Feb 13 '20 at 15:45
82

The best way I found:

Drawable leftDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_search);
search.setCompoundDrawablesWithIntrinsicBounds(leftDrawable, null, null, null);
Hani
  • 1,430
  • 15
  • 17
  • 9
    As of 8/25/17 this is the only thing that works for me (setting the drawable programmatically). I actually used: `Drawable drawable = VectorDrawableCompat.create(getResources(), status.getIconResId(), wrapper.getTheme()); statusButton.setCompoundDrawablesRelativeWithIntrinsicBounds(null, drawable, null, null);` – saiyancoder Aug 26 '17 at 01:04
  • 2
    minSdk for `setCompoundDrawablesWithIntrinsicBounds` is **17**. Otherwise this works great. – Victor Rendina Nov 29 '17 at 23:37
  • 2
    Its minSdk is 1 not 17, you probably look the the similar apis. setCompoundDrawablesWithIntrinsicBounds => minSdk 1; setCompoundDrawablesRelativeWithIntrinsicBounds => minSdk 17 – 正宗白布鞋 Dec 26 '17 at 19:18
  • Best solution for programmatically setting, +1 – Woton Sampaio Sep 14 '18 at 21:28
  • I used this with BindingAdapters and works effectively, thanks! – Ric17101 Oct 23 '18 at 12:04
  • Thanks! Yes, `setCompoundDrawablesWithIntrinsicBounds`, not `setCompoundDrawables`. – CoolMind Oct 22 '21 at 07:27
78

This solution is no longer correct. From 23.3.0 version vector drawables can only be loaded via app:srcCompat or setImageResource()

Try to wrap your vector drawable into layer-list or selector:

<TextView
    android:id="@+id/textView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:drawableRight="@drawable/ic_accessible_white_wrapped"
    android:background="@color/colorPrimary"
    android:textColor="#FFFFFF"
    android:textSize="22sp"
    android:text="Hello World!"/>

ic_accessible_white_wrapped.xml:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_accessible_white_36px"/>
</layer-list>
Alexandr Shutko
  • 1,857
  • 2
  • 20
  • 27
  • This works well. Thanks.. But it seems that you can't directly refer to an SVG in "android:drawableXXXXX" you have to wrap it in something else. – angryITguy Mar 03 '16 at 03:09
  • 1
    Yes. You can't use it directly. Only as app:srcCompat or wrapped or programmatically. This was described here: http://android-developers.blogspot.ru/2016/02/android-support-library-232.html – Alexandr Shutko Mar 03 '16 at 03:11
  • Ahh.. thanks.. I can see the paragraph you're referring to. So, is it still a bug if they tell you not to do it ? ;) – angryITguy Mar 03 '16 at 03:21
  • 1
    I think it is all about backward compatibility. It looks like there is some problem to get full svg support, so they made some workarounds... – Alexandr Shutko Mar 03 '16 at 03:24
  • Right but in your way I have to create twice as much of my all vector resources. – Behzad Bahmanyar Nov 08 '16 at 13:54
  • 2
    @HarishGyanani from support version 23.3.0, it's no longger support. You must add more command in your activity: static { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); } } – Cuong Nguyen Dec 08 '16 at 04:37
64

To complement some of the answers here: you can get VectorDrawable to work as drawableLeft (etc.) but it depends on the Support Library version and it comes with a price.

In which cases does it work? I've made this diagram to help (valid for Support Library 23.4.0 to - at least - 25.1.0).

VectorDrawable cheatsheet

David Ferrand
  • 5,357
  • 1
  • 33
  • 43
  • 2
    It's great but you should correct method name in static block to `setCompatVectorFromResourcesEnabled` – Vikas Patidar Apr 06 '17 at 08:11
  • 1
    setCompatVectorFromResourcesEnabled solution does not work on 25.3.1, unfortunately – Ilja S. Apr 28 '17 at 12:21
  • `From 23.3.0 version vector drawables can only be loaded via app:srcCompat or setImageResource()` so this solution is deprecated and won't work – user25 May 13 '18 at 07:54
  • enabling `setCompatVectorFromSourcesEnabled(true)` makes it possible to load vectordrawables in `android:background` on Android 4.x. So, thanks! (You do need to wrap the actual vector in a single item layer-list) – Peterdk Jan 07 '20 at 20:51
15

None of the other answers worked, here's how I've added a VectorDrawable to a TextView, you should use VectorDrawableCompat.create() when dealing with VectorDrawables below Android L:

TextView titleTextView = (TextView) viewHolder.getView(android.R.id.text1);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
       Drawable leftDrawable = AppCompatResources
                            .getDrawable(context, R.drawable.ic_tickbox);
       titleTextView.setCompoundDrawablesWithIntrinsicBounds(leftDrawable, null, null, null);
}
else
{
      //Safely create our VectorDrawable on pre-L android versions. 
       Drawable leftDrawable = VectorDrawableCompat
                            .create(context.getResources(), R.drawable.ic_tickbox, null);
       titleTextView.setCompoundDrawablesWithIntrinsicBounds(leftDrawable, null, null, null);
}

Short, sweet, and to the point!

Sakiboy
  • 7,252
  • 7
  • 52
  • 69
9

From Google: As of Android Support Library 23.3.0, support vector drawables can only be loaded via app:srcCompat or setImageResource()..

http://android-developers.blogspot.ru/2016/02/android-support-library-232.html

Ghasem
  • 14,455
  • 21
  • 138
  • 171
paulzeng
  • 99
  • 1
9

It is possible to directly set vector drawables in xml, but you have include the data binding framework.

Just write

<TextView
...
android:drawableRight="@{@drawable/ic_accessible_white_36px}"/>

and wrap your whole layout in a <layout> tag, so basically your xml would look like:

<?xml version="1.0" encoding="utf-8"?>
<layout>

    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="au.com.angryitguy.testsvg.MainActivity">


        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorPrimary"
            android:drawableRight="@{@drawable/ic_accessible_white_36px}"
            android:text="Hello World!"
            android:textColor="#FFFFFF"
            android:textSize="22sp"/>
    </RelativeLayout>
</layout>

To activate the data binding framework just add

android {
    ....
    defaultConfig {
        dataBinding {
            enabled = true
        }
    }
}

You don't have to use any other features of the binding library

EDIT:

Of course if you want to use vector drawables pre-Lollipop your have to enable support vector drawables using

vectorDrawables.useSupportLibrary = true

So your build.gradle needs 2 new commands:

android {
    ....
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
        dataBinding {
            enabled = true
        }
    }
}

thanks to rkmax for the remark

Community
  • 1
  • 1
Hans M
  • 109
  • 1
  • 2
  • This is a good idea, but you need to write a binding adapter for this to work. Here is a working example: https://gist.github.com/lisawray/78c33f76809d2bcbbec9983e2c141a70 – BladeCoder Oct 25 '16 at 19:35
  • 1
    I must say this works for me but you have to enable `vectorDrawables.useSupportLibrary` on app/build.gradle and also add AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) on the activity – rkmax Nov 04 '16 at 00:31
  • I forgot to add defaultConfig in the `build.gradle` that might be the reason why it isn't working – Hans M Dec 05 '16 at 07:39
  • Thanks - you're a life saver! – Van Oct 08 '18 at 22:19
  • This is an underrated answer! – DYS May 30 '19 at 10:08
6

I went thru all answers and using latest Android studio 3.0.1 and AppCompat Support Library 26.1.0 I can assure this just works fine.

In build.gradle (app) file

android {
    compileSdkVersion 26
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }
}

dependencies {
    implementation 'com.android.support:appcompat-v7:26.1.0'
}

And in Activity extending AppcompatActivity include this outside methods i.e. a static block

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}  

or if you want this to be applied to whole app just include this line inside the class extending Application class

override fun onCreate() {
    super.onCreate()
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
}

Textview in xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="vertical">

<TextView
        android:id="@+id/passName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:drawableLeft="@drawable/account_drawableleft_selector"
        android:drawablePadding="5dp"
        android:ellipsize="marquee"
        android:fontFamily="@font/montserrat_light_family"
        android:gravity="center_vertical"
        android:marqueeRepeatLimit="marquee_forever"
        android:paddingRight="10dp"
        android:scrollHorizontally="true"
        android:singleLine="true"
        android:textColor="@color/app_text_color"
        android:textSize="12sp"
        tools:text="Account Name" />
</LinearLayout>

account_drawableleft_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_account_circle_24dp" /> <!-- checked -->
</selector>
JJD
  • 50,076
  • 60
  • 203
  • 339
Amit Tumkur
  • 2,752
  • 26
  • 27
  • Works fine on what API? Did you try on 19 or 20? – Divers Feb 05 '18 at 14:25
  • Can you show how you use `drawableRight` for `TextView` in XML? – Divers Feb 07 '18 at 08:30
  • 2
    like few mentioned above, if u use vector xml directly as drawableLeft u still get this crash or exception thus workaround is to use that vector xml as a selector for drawableLeft for Textview. Usage u can see in edited answer. – Amit Tumkur Feb 07 '18 at 09:37
  • 1
    it's not going to work, you can't use vector drawable for TextView directly in xml – user25 May 13 '18 at 08:15
  • Test it in API 19, and 20. I used AppCompatDelegate.setCompatVectorFromResourcesEnabled in the application class instead. Working like a charm ! – Red M Aug 09 '18 at 15:18
  • Thanks. In my case I had to write `` instead of `` – CoolMind Aug 23 '18 at 11:36
6

As of androidx.appcompat:appcompat:1.1.0 you can use

app:drawableLeftCompat
app:drawableStartCompat
...
JJD
  • 50,076
  • 60
  • 203
  • 339
chezi shem tov
  • 428
  • 3
  • 8
5

I am so late to answer this question as I stucked late with this problem. I had the same issue with svg/vector drawables with TextView. Instead of making your own custom drawable I am able to fix my problem with 2 lines of code as below:

Drawable drawableTop = AppCompatResources.getDrawable(view.getContext(), iconId);
view.setCompoundDrawablesWithIntrinsicBounds(null, drawableTop, null, null);

Hope it will help you out.

Rahul Sharma
  • 12,515
  • 6
  • 21
  • 30
4

I've designed a tiny library for this - textview-rich-drawable (it also supports defining the compound-drawables' size and tinting).

<com.tolstykh.textviewrichdrawable.TextViewRichDrawable
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Some text"
    app:compoundDrawableHeight="24dp"
    app:compoundDrawableWidth="24dp"
    app:drawableTopVector="@drawable/some_vector_drawble"
    app:drawableEndVector="@drawable/another_vector_drawable"
    app:drawableTint="@color/colorAccent" />

And the dependency

compile 'com.tolstykh.textviewrichdrawable:textview-rich-drawable:0.3.2'

enter image description here

Oleksandr
  • 6,226
  • 1
  • 46
  • 54
3

If you are using binding, there is another magic way to have the same approach to use vectors in a TextView. Wrapping them like:

android:drawableLeft="@{@drawable/vector_ic_access_time_24px}"
android:drawableStart="@{@drawable/vector_ic_access_time_24px}"

That will magically work, I haven't investigated what's happening behind the scenes, but I guess the TextView is using the getDrawable method from the AppCompatResources or similar.

cesards
  • 15,882
  • 11
  • 70
  • 65
2

In build.gradle (app) file

android {
    compileSdkVersion 26
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }

    dataBinding {
        enabled = true
    }
}

...

public class BindingUtils {
    @BindingAdapter("android:drawableRight")
    public static void setDrawableStart(TextView textView, int resourceId) {
        Drawable drawable = AppCompatResources.getDrawable(textView.getContext(), resourceId);
        Drawable[] drawables = textView.getCompoundDrawables();
        textView.setCompoundDrawablesWithIntrinsicBounds(drawable,
                drawables[1], drawables[2], drawables[3]);
    } 
}

use (When Databinding)

android:drawableRight="@{viewModel.ResId}"

Or (Normal)

android:drawableRight="@{@drawable/ic_login_24dp}"
JJD
  • 50,076
  • 60
  • 203
  • 339
Ahmad Aghazadeh
  • 16,571
  • 12
  • 101
  • 98
2

Based on Behzad Bahmanyar's answer, I noticed that I could not use android's normal attributes for normal png files:

android:drawableTop
android:drawableBottom
etc

because it would be replaced with null in

Drawable drawableTop = null;
...
setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStart, drawableTop, drawableEnd, drawableBottom);

if the app:drawableTopCompat was not set, but android:drawableTop was (for eg).

Here is the full solution:

public class CustomTextView extends AppCompatTextView {
    private static final int NOT_SET = -1;
    private static final int LEFT = 0;
    private static final int START = 0;
    private static final int TOP = 1;
    private static final int RIGHT = 2;
    private static final int END = 2;
    private static final int BOTTOM = 3;

    public CustomTextView(Context context) {
        super(context);
    }

    public CustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initAttrs(context, attrs);
    }

    public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttrs(context, attrs);
    }

    void initAttrs(Context context, AttributeSet attrs) {
        if (attrs == null) {
            return;
        }
        Drawable[] drawablesArr = getCompoundDrawables();

        TypedArray attributeArray = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
        Drawable drawableStart = null;
        Drawable drawableEnd = null;
        Drawable drawableBottom = null;
        Drawable drawableTop = null;
        Drawable drawableLeft = null;
        Drawable drawableRight = null;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            drawableStart = attributeArray.getDrawable(R.styleable.CustomTextView_drawableStartCompat);
            drawableEnd = attributeArray.getDrawable(R.styleable.CustomTextView_drawableEndCompat);
            drawableBottom = attributeArray.getDrawable(R.styleable.CustomTextView_drawableBottomCompat);
            drawableTop = attributeArray.getDrawable(R.styleable.CustomTextView_drawableTopCompat);
            drawableLeft = attributeArray.getDrawable(R.styleable.CustomTextView_drawableLeftCompat);
            drawableRight = attributeArray.getDrawable(R.styleable.CustomTextView_drawableRightCompat);
        } else {
            final int drawableStartId = attributeArray.getResourceId(R.styleable.CustomTextView_drawableStartCompat, NOT_SET);
            final int drawableEndId = attributeArray.getResourceId(R.styleable.CustomTextView_drawableEndCompat, NOT_SET);
            final int drawableBottomId = attributeArray.getResourceId(R.styleable.CustomTextView_drawableBottomCompat, NOT_SET);
            final int drawableTopId = attributeArray.getResourceId(R.styleable.CustomTextView_drawableTopCompat, NOT_SET);
            final int drawableLeftId = attributeArray.getResourceId(R.styleable.CustomTextView_drawableLeftCompat, NOT_SET);
            final int drawableRightId = attributeArray.getResourceId(R.styleable.CustomTextView_drawableRightCompat, NOT_SET);

            if (drawableStartId != NOT_SET)
                drawableStart = AppCompatResources.getDrawable(context, drawableStartId);
            if (drawableLeftId != NOT_SET)
                drawableLeft = AppCompatResources.getDrawable(context, drawableLeftId);
            if (drawableEndId != NOT_SET)
                drawableEnd = AppCompatResources.getDrawable(context, drawableEndId);
            if (drawableRightId != NOT_SET)
                drawableRight = AppCompatResources.getDrawable(context, drawableRightId);
            if (drawableBottomId != NOT_SET)
                drawableBottom = AppCompatResources.getDrawable(context, drawableBottomId);
            if (drawableTopId != NOT_SET)
                drawableTop = AppCompatResources.getDrawable(context, drawableTopId);
        }

        drawableStart = (drawableStart != null ? drawableStart : drawablesArr[START]);
        drawableLeft = (drawableLeft != null ? drawableLeft : drawablesArr[LEFT]);
        drawableStart = (drawableStart != null ? drawableStart : drawableLeft);

        drawableEnd = (drawableEnd != null ? drawableEnd : drawablesArr[END]);
        drawableRight = (drawableRight != null ? drawableRight : drawablesArr[RIGHT]);
        drawableEnd = (drawableEnd != null ? drawableEnd : drawableRight);

        drawableBottom = (drawableBottom != null ? drawableBottom : drawablesArr[BOTTOM]);
        drawableTop = (drawableTop != null ? drawableTop : drawablesArr[TOP]);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStart, drawableTop, drawableEnd, drawableBottom);
        } else {
            setCompoundDrawables(drawableStart, drawableTop, drawableEnd, drawableBottom);
        }

        attributeArray.recycle();
    }
}
Goran Horia Mihail
  • 3,536
  • 2
  • 29
  • 40
  • This should be accepted answer. work with normal png files and attributes drawableTop, drawableBottom .The accepted answer not work with png file and drawableTop,drawableBottom attributes. – Bhargav Pandya Nov 12 '18 at 14:42
1

Using Vector Drawables use

kotlin

 val drawable1 = VectorDrawableCompat.create(resources, R.drawable.ic_rb_username, theme)
        yourView.setCompoundDrawablesRelativeWithIntrinsicBounds( drawable1, null, null, null)

java

  Drawable drawable1 = VectorDrawableCompat.create(getResources(), R.drawable.ic_rb_username, getTheme());
        yourView.setCompoundDrawablesRelativeWithIntrinsicBounds( drawable1, null, null, null);
MarGin
  • 2,078
  • 1
  • 17
  • 28
1

For backward compatibility use:

TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, left, top, right, bottom)
Shayan_Aryan
  • 2,002
  • 1
  • 29
  • 31
0

Im using Binding adapter for solving this Problem

@JvmStatic
@BindingAdapter("drawableSvgLeft")
fun addDrawableSvgLeft(textView: TextView,drawable: Drawable){
    textView.setCompoundDrawablesWithIntrinsicBounds(drawable,null,null,null)
}

@JvmStatic
@BindingAdapter("drawableSvgRight")
fun addDrawableSvgRight(textView: TextView,drawable: Drawable){
    textView.setCompoundDrawablesWithIntrinsicBounds(null,null,drawable,null)
}

and also using in this way in my layout

<TextView
  drawableSvgRight="@{@drawable/svg_ic_battle_trophy}"
  .....

probably vectorDrawables.useSupportLibrary = true in default config is necessary

mahdi ZTD
  • 1
  • 1