ChatGPT解决这个技术问题 Extra ChatGPT

Is it possible to use VectorDrawable in Buttons and TextViews using android:DrawableRight?

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) 
...
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).
To see how use VectorDrawable with drawableLeft, drawableRight, drawableTop, drawableBottom check out This answer
I found this works for me : android.jlelse.eu/…

B
Behzad Bahmanyar

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" : 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: app:drawableEndCompat="@drawable/your_vector_drawable" app:drawableTopCompat="@drawable/your_vector_drawable" app:drawableBottomCompat="@drawable/your_vector_drawable" /> You can do similar thing with Button, EditText and RadioButton because they derived from TextView

Hope this helps :)


Very useful answer. Anyway, I recommend reading Dadou's very complete answer too. Removing vectorDrawables { useSupportLibrary = true } from my build.gradle as that answer suggests worked for me.
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]
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()
How to add app:drawableEndCompat for better RTL support? Cause setCompoundDrawablesRelativeWithIntrinsicBounds needs API level 17 at least.
What about setting the drawable programmatically?
H
Hani

The best way I found:

Drawable leftDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_search);
search.setCompoundDrawablesWithIntrinsicBounds(leftDrawable, null, null, null);

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);
minSdk for setCompoundDrawablesWithIntrinsicBounds is 17. Otherwise this works great.
Its minSdk is 1 not 17, you probably look the the similar apis. setCompoundDrawablesWithIntrinsicBounds => minSdk 1; setCompoundDrawablesRelativeWithIntrinsicBounds => minSdk 17
Best solution for programmatically setting, +1
I used this with BindingAdapters and works effectively, thanks!
A
Alexandr Shutko

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>

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.
Yes. You can't use it directly. Only as app:srcCompat or wrapped or programmatically. This was described here: android-developers.blogspot.ru/2016/02/…
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 ? ;)
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...
@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); } }
D
David Ferrand

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).

https://i.stack.imgur.com/1xUPY.png


It's great but you should correct method name in static block to setCompatVectorFromResourcesEnabled
setCompatVectorFromResourcesEnabled solution does not work on 25.3.1, unfortunately
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
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)
S
Sakiboy

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!


This worked for Android P, and min API level 26 (target 28)
@MiStr - It’s also backwards compatible :)
A
Alex Jolig

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


that means no solution for now
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="schemas.android.com/apk/res/android"> <item android:drawable="@drawable/ic_accessible_white_36px"/> </layer-list>
C
Community

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


This 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
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
I forgot to add defaultConfig in the build.gradle that might be the reason why it isn't working
Thanks - you're a life saver!
This is an underrated answer!
J
JJD

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>

Works fine on what API? Did you try on 19 or 20?
Can you show how you use drawableRight for TextView in XML?
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.
it's not going to work, you can't use vector drawable for TextView directly in xml
Test it in API 19, and 20. I used AppCompatDelegate.setCompatVectorFromResourcesEnabled in the application class instead. Working like a charm !
J
JJD

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

app:drawableLeftCompat
app:drawableStartCompat
...

seems like this attributes do not work with databinding?
R
Rahul Sharma

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.


Not compatible with pre L devices.
@Sakiboy Yeah it is , as I am using this code with minimum api 17.
Doesn'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.
O
Oleksandr

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'

https://i.stack.imgur.com/T3CpM.png


c
cesards

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.


G
Goran Horia Mihail

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();
    }
}

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.
J
JJD

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}"

M
MarGin

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);

S
Shayan_Aryan

For backward compatibility use:

TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, left, top, right, bottom)

m
mahdi ZTD

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


关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now