Is it possible to use VectorDrawable in Buttons and TextViews using android:DrawableRight?
Solution 1
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:
-
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>
-
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(); } } }
-
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
Hope this helps :)
Solution 2
The best way I found:
Drawable leftDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_search);
search.setCompoundDrawablesWithIntrinsicBounds(leftDrawable, null, null, null);
Solution 3
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>
Solution 4
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).
Solution 5
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!
angryITguy
Updated on February 14, 2020Comments
-
angryITguy over 4 years
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) ...
-
angryITguy about 8 yearsThis 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.
-
Alexandr Shutko about 8 yearsYes. You can't use it directly. Only as app:srcCompat or wrapped or programmatically. This was described here: android-developers.blogspot.ru/2016/02/…
-
angryITguy about 8 yearsAhh.. thanks.. I can see the paragraph you're referring to. So, is it still a bug if they tell you not to do it ? ;)
-
Alexandr Shutko about 8 yearsI think it is all about backward compatibility. It looks like there is some problem to get full svg support, so they made some workarounds...
-
Shubham AgaRwal over 7 yearsthat means no solution for now
-
BladeCoder over 7 yearsThis is a good idea, but you need to write a binding adapter for this to work. Here is a working example: gist.github.com/lisawray/78c33f76809d2bcbbec9983e2c141a70
-
rkmax over 7 yearsI 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 -
Behzad Bahmanyar over 7 yearsRight but in your way I have to create twice as much of my all vector resources.
-
Hans M over 7 yearsI forgot to add defaultConfig in the
build.gradle
that might be the reason why it isn't working -
Cuong Nguyen over 7 years@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); } }
-
Vikas Patidar about 7 yearsIt's great but you should correct method name in static block to
setCompatVectorFromResourcesEnabled
-
Ilja S. about 7 yearssetCompatVectorFromResourcesEnabled solution does not work on 25.3.1, unfortunately
-
Sakiboy about 7 yearsNot compatible with pre
L
devices. -
Rahul Sharma about 7 years@Sakiboy Yeah it is , as I am using this code with minimum api 17.
-
Sakiboy about 7 yearsDoesn't work with
VectorDrawables
on all devices running pre-L, just sayin. Be cautious when using this answer, as there are safer more accurate APIs to use. -
Sam almost 7 yearsVery useful answer. Anyway, I recommend reading Dadou's very complete answer too. Removing
vectorDrawables { useSupportLibrary = true }
from mybuild.gradle
as that answer suggests worked for me. -
saiyancoder over 6 yearsAs 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);
-
Ramdane Oualitsen over 6 yearsI 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]
-
Heinous Games over 6 yearsI 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 theapp:srcCompat
via XML or set it via code withVectorDrawableCompat.create()
-
Victor Rendina over 6 yearsminSdk for
setCompoundDrawablesWithIntrinsicBounds
is 17. Otherwise this works great. -
正宗白布鞋 over 6 yearsIts minSdk is 1 not 17, you probably look the the similar apis. setCompoundDrawablesWithIntrinsicBounds => minSdk 1; setCompoundDrawablesRelativeWithIntrinsicBounds => minSdk 17
-
Dr.jacky over 6 yearsHow to add
app:drawableEndCompat
for better RTL support? CausesetCompoundDrawablesRelativeWithIntrinsicBounds
needs API level 17 at least. -
Dr.jacky over 6 yearsThe manual width and height in XML, doesn't work. You have to add a custom attribute for
width/height
. -
Null Pointer Exception over 6 yearsTry 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="schemas.android.com/apk/res/android"> <item android:drawable="@drawable/ic_accessible_white_36px"/> </layer-list>
-
Divers over 6 yearsWorks fine on what API? Did you try on 19 or 20?
-
Divers over 6 yearsCan you show how you use
drawableRight
forTextView
in XML? -
Amit Tumkur over 6 yearslike 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.
-
CoolMind over 6 yearsThanks. Don't forget to wrap TypedArray settings in try-finally block. Also -1 can be replaced with 0.
-
user25 about 6 years
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 about 6 yearsit's not going to work, you can't use vector drawable for TextView directly in xml
-
Red M almost 6 yearsTest it in API 19, and 20. I used AppCompatDelegate.setCompatVectorFromResourcesEnabled in the application class instead. Working like a charm !
-
CoolMind over 5 yearsThanks. In my case I had to write
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
instead of<selector …>
-
Woton Sampaio over 5 yearsBest solution for programmatically setting, +1
-
MiStr over 5 yearsThis worked for Android P, and min API level 26 (target 28)
-
Sakiboy over 5 years@MiStr - It’s also backwards compatible :)
-
Van over 5 yearsThanks - you're a life saver!
-
Ric17101 over 5 yearsI used this with BindingAdapters and works effectively, thanks!
-
Bhargav Pandya over 5 yearsThis 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.
-
Arsenius about 5 yearsit still doesn't support for AppCompatButton :(
-
android developer about 5 yearsWhat about setting the drawable programmatically?
-
DYS almost 5 yearsThis is an underrated answer!
-
hjchin almost 5 yearsThe link as of july 2019 does not contain information of supporting vector, here is the link that shows that. developer.android.com/jetpack/androidx/releases/…
-
Peterdk over 4 yearsenabling
setCompatVectorFromSourcesEnabled(true)
makes it possible to load vectordrawables inandroid:background
on Android 4.x. So, thanks! (You do need to wrap the actual vector in a single item layer-list) -
DemoDemo over 4 yearsFor 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
-
Zijian Wang almost 4 yearsseems like this attributes do not work with databinding?
-
CoolMind over 2 yearsThanks! Yes,
setCompoundDrawablesWithIntrinsicBounds
, notsetCompoundDrawables
.