如何在Android中设置一个按钮内的矢量绘制大小?

时间:2022-01-23 09:33:35

The Android Studio Vector Assets tools convert vector drawable to PNG-s for devices pre-Lollipop but I get really bad quality PNG-s as you can see here:

Android Studio Vector Assets工具将Vector drawable转换为PNG-s,用于pre-Lollipop设备,但是我得到的PNG-s质量很差,正如你在这里看到的:

如何在Android中设置一个按钮内的矢量绘制大小?

What's more is that the button's background solid color is supposed to be this light green that you see on the left but the drawable overwrite it:

更重要的是,按钮的背景色应该是你在左边看到的浅绿色但可绘制覆盖:

<item android:state_checked="true"
    android:drawable="@drawable/show">
    <shape android:shape="rectangle">
        <corners android:bottomRightRadius="8dp"/>
        <solid android:color="@color/waveComponentGreen"/>
    </shape>
</item>

<item android:state_checked="false"
    android:drawable="@drawable/hide">
    <shape android:shape="rectangle">
        <corners android:bottomRightRadius="8dp"/>
        <solid android:color="@color/waveComponentGreen"/>
    </shape>
</item>

The xml for the drawable is (the default from the material icons):

可绘制的xml(材料图标的默认格式):

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
<path
    android:fillColor="#FF000000"
    android:pathData="M8.59,16.34l4.58,-4.59 -4.58,-4.59L10,5.75l6,6 -6,6z"/>

I wanted also to make the icon appear a bit smaller by tweaking the values and I noticed increasing the viewport dimensions decreases the icon but I'm not sure I understand why.

我还想通过调整值使图标显得更小一些,我注意到增加viewport维度会减少图标,但我不确定我能理解为什么。

So: How do I make the icon and the generated PNG appear smaller, less blurry and with the background colour set in the resource file? Thank you.

那么:如何使图标和生成的PNG看起来更小、更不模糊,并在资源文件中设置背景颜色?谢谢你!

EDIT: I managed to get the solid colour background with the icon by combining them in a separate xml file with layer-lists:

编辑:我通过将它们与一个单独的xml文件合并在一个列列表中,从而获得了这个图标的纯色背景:

<layer-list
xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
    <shape android:shape="rectangle">
        <corners android:bottomRightRadius="10dp"/>
        <solid android:color="@color/waveComponentGreen"/>
    </shape>
</item>
<item android:drawable="@drawable/show"
    android:top="10dp"
    android:bottom="10dp"
    android:left="10dp"
    android:right="10dp"
    />

The result is:

其结果是:

如何在Android中设置一个按钮内的矢量绘制大小?

I managed to reduce the blurring by increasing the width and height of the vector drawable. However without the android:top|bottom|left|right tags, the drawable is stretched across the whole area of the button. The second button doesn't need to have a background solid color so I'm not using the layer-list tags => no way to set a top|bottom|left|right margin for the drawable.
If I reduce the button size what I'm doing is reducing the clickable area of the button.

我通过增加矢量绘图的宽度和高度来减少模糊。然而,没有android:顶部|底部|左|右标签,可拉伸的部分会延伸到整个按钮区域。第二个按钮不需要有一个背景的纯色,所以我没有使用layer-list标签=>没有办法设置|底部|的|的右边界为drawable。如果我减少按钮的大小我所做的就是减少按钮的可点击区域。

My updated question is how to set the size of the vector drawable inside a button/toggle button/radio button without reducing the size of the button itself?

我的更新问题是如何设置一个按钮/切换按钮/单选按钮的矢量大小,而不减少按钮本身的大小?

UPDATE
I couldn't find a way to resize the vector drawable on pre-API 21 devices. So instead I made the buttons themselves smaller and increased the touch area of each button.

更新我找不到一种方法来调整预api 21设备上的矢量绘制大小。因此,我让按钮本身更小,并增加了每个按钮的触摸面积。

2 个解决方案

#1


10  

The correct approach to scale any drawable would be to use vectorDrawable.setBounds(left,top,right,bottom) , but unfortunately that does not work for vector drawables (why Google ?).

缩放任何可绘制对象的正确方法是使用vectorDrawable.setBounds(左、上、右、下),但不幸的是,这不适用于向量可绘制对象(为什么是谷歌?)

So as a workaround I load my vector drawables , convert them to bitmap drawable and that will allow us to use the setBounds method on the bitmap drawable. Note that you are scaling bitmaps here , so you can lose some sharpness of the image. I mainly use those methods when I need to use my drawable as a compound drawable of a text view or a button for example.

因此,作为一个解决方案,我加载我的向量可绘制文件,将它们转换为位图可绘制,这将允许我们在位图可绘制文件上使用setBounds方法。注意,您正在缩放位图,因此您可能会失去图像的一些清晰度。当我需要将drawable作为文本视图或按钮的复合drawable时,我主要使用这些方法。

I ended up writing a helper class that will load a vector drawable set a tint to it and return a bitmap drawable that you can actually scale and tint as you wish. I've tested it for API levels 19 up to 23 , and it works.

我最后编写了一个助手类,它将加载一个向量绘制集,并返回一个位图绘制,您可以按自己的意愿缩放和着色。我已经对它进行了API级别19到23的测试,它是有效的。

Don't forget to use vectorDrawables.useSupportLibrary = true in your build.gradle.

不要忘记使用vectorDrawables。useSupportLibrary =在你的建筑物中为真。

public class VectorDrawableUtils {

/**
 * Gets a Bitmap from provided Vector Drawable image
 *
 * @param vd VectorDrawable
 * @return Bitmap
 */
public static Optional<Bitmap> createBitmapFromVectorDrawable(final @NonNull Drawable vd) {
    try {
        Bitmap bitmap;
        bitmap = Bitmap.createBitmap(vd.getIntrinsicWidth(), vd.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        vd.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        vd.draw(canvas);
        return Optional.of(bitmap);
    } catch (OutOfMemoryError e) {
        Injector.getDependency(getContext(), IEventTracker.class).logHandledException(e);
        return Optional.empty();
    }
}

/**
 * Loads vector drawable and apply tint color on it.
 */
public static Drawable loadVectorDrawableWithTintColor(final @DrawableRes int vdRes,
                                                       final @ColorRes int clrRes,final Context context) {
    Drawable drawable = ContextCompat.getDrawable(context, vdRes);
    DrawableCompat.setTint(drawable, getContext().getResources().getColor(clrRes));
    return drawable;
}

/**
 * Converts given vector drawable to Bitmap drawable
 */
public static BitmapDrawable convertVectorDrawableToBitmapDrawable(final @NonNull Drawable vd) {
    //it is safe to create empty bitmap drawable from null source
    return new BitmapDrawable(createBitmapFromVectorDrawable(vd).get());
}

/**
 * Loads vector drawable , aplys tint on it and returns a wrapped bitmap drawable.
 * Bitmap drawable can be resized using setBounds method (unlike the VectorDrawable)
 * @param context Requires view context !
 */
public static Drawable loadVectorDrawableWithTint(
        final @DrawableRes int vectorDrawableRes, final @ColorRes int colorRes,final Context context) {
    Drawable vd = VectorDrawableUtils.loadVectorDrawableWithTintColor(vectorDrawableRes,
            colorRes, context);
    final BitmapDrawable bitmapDrawable = VectorDrawableUtils.convertVectorDrawableToBitmapDrawable(vd);
    ColorStateList tint = ContextCompat.getColorStateList(context,colorRes);
    final Drawable wrappedDrawable = DrawableCompat.wrap(bitmapDrawable);
    DrawableCompat.setTintList(wrappedDrawable,tint);
    return wrappedDrawable;
    }
}

Now I would use this helper class like this :

现在我要用这个助手类

    Drawable bd = VectorDrawableUtils.loadVectorDrawableWithTint(
                R.drawable.ic_dropdown, R.color.black,getContext());
        bd.setBounds(0, 0, textView.getMeasuredHeight(), textView.getMeasuredHeight());
        textView.setCompoundDrawablesWithIntrinsicBounds(null, null, bd, null);

It is important to use a Context of a View or Activity, not the Application context! Hope it will solve your problem, or help someone else. And if someone has a better and cleaner solution, I am interested to know as well.

重要的是使用视图或活动的上下文,而不是应用程序上下文!希望它能解决你的问题,或者帮助别人。如果有人有更好更干净的解决方案,我也很想知道。

#2


0  

MyTextView class:

MyTextView类:

public class MyTextView extends AppCompatTextView {

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

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

public MyTextView(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.MyTextView);

        int defaultWidthHeight = 0;
        int widthHeight = 0;
        Drawable drawableLeft = null;
        Drawable drawableStart = null;
        Drawable drawableRight = null;
        Drawable drawableEnd = null;
        Drawable drawableBottom = null;
        Drawable drawableTop = null;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            drawableLeft = attributeArray.getDrawable(R.styleable.MyTextView_drawableLeftCompatTextView);
            drawableStart = attributeArray.getDrawable(R.styleable.MyTextView_drawableStartCompatTextView);
            drawableRight = attributeArray.getDrawable(R.styleable.MyTextView_drawableRightCompatTextView);
            drawableEnd = attributeArray.getDrawable(R.styleable.MyTextView_drawableEndCompatTextView);
            drawableBottom = attributeArray.getDrawable(R.styleable.MyTextView_drawableBottomCompatTextView);
            drawableTop = attributeArray.getDrawable(R.styleable.MyTextView_drawableTopCompatTextView);
        } else {
            final int drawableLeftId = attributeArray.getResourceId(R.styleable.MyTextView_drawableLeftCompatTextView, -1);
            final int drawableStartId = attributeArray.getResourceId(R.styleable.MyTextView_drawableStartCompatTextView, -1);
            final int drawableRightId = attributeArray.getResourceId(R.styleable.MyTextView_drawableRightCompatTextView, -1);
            final int drawableEndId = attributeArray.getResourceId(R.styleable.MyTextView_drawableEndCompatTextView, -1);
            final int drawableBottomId = attributeArray.getResourceId(R.styleable.MyTextView_drawableBottomCompatTextView, -1);
            final int drawableTopId = attributeArray.getResourceId(R.styleable.MyTextView_drawableTopCompatTextView, -1);

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

        if(!attributeArray.hasValue(R.styleable.MyTextView_drawableWidthHeightCompatTextView)) {
            if (attributeArray.hasValue(R.styleable.MyTextView_drawableLeftCompatTextView)) {
                defaultWidthHeight = drawableLeft.getIntrinsicWidth();
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableStartCompatTextView)) {
                defaultWidthHeight = drawableStart.getIntrinsicWidth();
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableRightCompatTextView)) {
                defaultWidthHeight = drawableRight.getIntrinsicWidth();
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableEndCompatTextView)) {
                defaultWidthHeight = drawableEnd.getIntrinsicWidth();
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableBottomCompatTextView)) {
                defaultWidthHeight = drawableBottom.getIntrinsicWidth();
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableTopCompatTextView)) {
                defaultWidthHeight = drawableTop.getIntrinsicWidth();
            }

            widthHeight = attributeArray.getInt(R.styleable.MyTextView_drawableWidthHeightCompatTextView, defaultWidthHeight);
        } else
            widthHeight = attributeArray.getInt(R.styleable.MyTextView_drawableWidthHeightCompatTextView, defaultWidthHeight);

        if(attributeArray.hasValue(R.styleable.MyTextView_drawableColorCompatTextView)){
            ColorStateList tintColor = attributeArray.getColorStateList(R.styleable.MyTextView_drawableColorCompatTextView);
            if (attributeArray.hasValue(R.styleable.MyTextView_drawableLeftCompatTextView)) {
                //drawableLeft.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                DrawableCompat.setTintList(drawableLeft, tintColor);
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableStartCompatTextView)) {
                //drawableStart.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                DrawableCompat.setTintList(drawableStart, tintColor);
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableRightCompatTextView)) {
                //drawableRight.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                DrawableCompat.setTintList(drawableRight, tintColor);
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableEndCompatTextView)) {
                //drawableEnd.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                DrawableCompat.setTintList(drawableEnd, tintColor);
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableBottomCompatTextView)) {
                //drawableBottom.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                DrawableCompat.setTintList(drawableBottom, tintColor);
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableTopCompatTextView)) {
                //drawableTop.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                DrawableCompat.setTintList(drawableTop, tintColor);
            }
        }

        WrappedDrawable drawableLeftWrapped = new WrappedDrawable(drawableLeft);
        drawableLeftWrapped.setBounds(0, 0, widthHeight, widthHeight);
        WrappedDrawable drawableStartWrapped = new WrappedDrawable(drawableStart);
        drawableStartWrapped.setBounds(0, 0, widthHeight, widthHeight);
        WrappedDrawable drawableRightWrapped = new WrappedDrawable(drawableRight);
        drawableRightWrapped.setBounds(0, 0, widthHeight, widthHeight);
        WrappedDrawable drawableEndWrapped = new WrappedDrawable(drawableEnd);
        drawableEndWrapped.setBounds(0, 0, widthHeight, widthHeight);
        WrappedDrawable drawableBottomWrapped = new WrappedDrawable(drawableBottom);
        drawableBottomWrapped.setBounds(0, 0, widthHeight, widthHeight);
        WrappedDrawable drawableTopWrapped = new WrappedDrawable(drawableTop);
        drawableTopWrapped.setBounds(0, 0, widthHeight, widthHeight);

        setCompoundDrawablesWithIntrinsicBounds(drawableLeftWrapped, drawableTopWrapped, drawableRightWrapped, drawableBottomWrapped);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
            setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStartWrapped, drawableTopWrapped, drawableEndWrapped, drawableBottomWrapped);

        attributeArray.recycle();
    }
}

class WrappedDrawable extends Drawable {

    private final Drawable _drawable;
    protected Drawable getDrawable() {
        return _drawable;
    }

    public WrappedDrawable(Drawable drawable) {
        super();
        _drawable = drawable;
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        //update bounds to get correctly
        super.setBounds(left, top, right, bottom);
        Drawable drawable = getDrawable();
        if (drawable != null) {
            drawable.setBounds(left, top, right, bottom);
        }
    }

    @Override
    public void setAlpha(int alpha) {
        Drawable drawable = getDrawable();
        if (drawable != null) {
            drawable.setAlpha(alpha);
        }
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        Drawable drawable = getDrawable();
        if (drawable != null) {
            drawable.setColorFilter(colorFilter);
        }
    }

    @Override
    public int getOpacity() {
        Drawable drawable = getDrawable();
        return drawable != null
                ? drawable.getOpacity()
                : PixelFormat.UNKNOWN;
    }

    @Override
    public void draw(Canvas canvas) {
        Drawable drawable = getDrawable();
        if (drawable != null) {
            drawable.draw(canvas);
        }
    }

    @Override
    public int getIntrinsicWidth() {
        Drawable drawable = getDrawable();
        return drawable != null
                ? drawable.getBounds().width()
                : 0;
    }

    @Override
    public int getIntrinsicHeight() {
        Drawable drawable = getDrawable();
        return drawable != null ?
                drawable.getBounds().height()
                : 0;
    }
}
}

attrs.xml:

attrs.xml:

<declare-styleable name="MyTextView">
    <attr name="drawableColorCompatTextView" format="reference|color"/>
    <attr name="drawableWidthHeightCompatTextView" format="integer"/>
    <attr name="drawableLeftCompatTextView" format="reference"/>
    <attr name="drawableStartCompatTextView" format="reference"/>
    <attr name="drawableRightCompatTextView" format="reference"/>
    <attr name="drawableEndCompatTextView" format="reference"/>
    <attr name="drawableTopCompatTextView" format="reference"/>
    <attr name="drawableBottomCompatTextView" format="reference"/>
</declare-styleable>

Usage:

用法:

<com.packagename.MyTextView
    android:id="@+id/txtUserName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:drawableLeftCompatTextView="@drawable/ic_username"
    app:drawableStartCompatTextView="@drawable/ic_username"
    app:drawableWidthHeightCompatTextView="48"
    app:drawableColorCompatTextView="@color/blue" />

Note: The only issue here is an unmodified vector(drawableWidthHeightCompatTextView didn't use), which vector's width and height are 24,
Is not equal in size on the device, with resized vector(vector's width and height are 12 and drawableWidthHeightCompatTextView="24").

注意:这里唯一的问题是一个未修改的向量(drawableWidthHeightCompatTextView没有使用),该向量的宽度和高度为24,在设备上的大小不相等,且具有调整大小的向量(向量的宽度和高度为12,drawwidthheightcompattextview =“24”)。

#1


10  

The correct approach to scale any drawable would be to use vectorDrawable.setBounds(left,top,right,bottom) , but unfortunately that does not work for vector drawables (why Google ?).

缩放任何可绘制对象的正确方法是使用vectorDrawable.setBounds(左、上、右、下),但不幸的是,这不适用于向量可绘制对象(为什么是谷歌?)

So as a workaround I load my vector drawables , convert them to bitmap drawable and that will allow us to use the setBounds method on the bitmap drawable. Note that you are scaling bitmaps here , so you can lose some sharpness of the image. I mainly use those methods when I need to use my drawable as a compound drawable of a text view or a button for example.

因此,作为一个解决方案,我加载我的向量可绘制文件,将它们转换为位图可绘制,这将允许我们在位图可绘制文件上使用setBounds方法。注意,您正在缩放位图,因此您可能会失去图像的一些清晰度。当我需要将drawable作为文本视图或按钮的复合drawable时,我主要使用这些方法。

I ended up writing a helper class that will load a vector drawable set a tint to it and return a bitmap drawable that you can actually scale and tint as you wish. I've tested it for API levels 19 up to 23 , and it works.

我最后编写了一个助手类,它将加载一个向量绘制集,并返回一个位图绘制,您可以按自己的意愿缩放和着色。我已经对它进行了API级别19到23的测试,它是有效的。

Don't forget to use vectorDrawables.useSupportLibrary = true in your build.gradle.

不要忘记使用vectorDrawables。useSupportLibrary =在你的建筑物中为真。

public class VectorDrawableUtils {

/**
 * Gets a Bitmap from provided Vector Drawable image
 *
 * @param vd VectorDrawable
 * @return Bitmap
 */
public static Optional<Bitmap> createBitmapFromVectorDrawable(final @NonNull Drawable vd) {
    try {
        Bitmap bitmap;
        bitmap = Bitmap.createBitmap(vd.getIntrinsicWidth(), vd.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        vd.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        vd.draw(canvas);
        return Optional.of(bitmap);
    } catch (OutOfMemoryError e) {
        Injector.getDependency(getContext(), IEventTracker.class).logHandledException(e);
        return Optional.empty();
    }
}

/**
 * Loads vector drawable and apply tint color on it.
 */
public static Drawable loadVectorDrawableWithTintColor(final @DrawableRes int vdRes,
                                                       final @ColorRes int clrRes,final Context context) {
    Drawable drawable = ContextCompat.getDrawable(context, vdRes);
    DrawableCompat.setTint(drawable, getContext().getResources().getColor(clrRes));
    return drawable;
}

/**
 * Converts given vector drawable to Bitmap drawable
 */
public static BitmapDrawable convertVectorDrawableToBitmapDrawable(final @NonNull Drawable vd) {
    //it is safe to create empty bitmap drawable from null source
    return new BitmapDrawable(createBitmapFromVectorDrawable(vd).get());
}

/**
 * Loads vector drawable , aplys tint on it and returns a wrapped bitmap drawable.
 * Bitmap drawable can be resized using setBounds method (unlike the VectorDrawable)
 * @param context Requires view context !
 */
public static Drawable loadVectorDrawableWithTint(
        final @DrawableRes int vectorDrawableRes, final @ColorRes int colorRes,final Context context) {
    Drawable vd = VectorDrawableUtils.loadVectorDrawableWithTintColor(vectorDrawableRes,
            colorRes, context);
    final BitmapDrawable bitmapDrawable = VectorDrawableUtils.convertVectorDrawableToBitmapDrawable(vd);
    ColorStateList tint = ContextCompat.getColorStateList(context,colorRes);
    final Drawable wrappedDrawable = DrawableCompat.wrap(bitmapDrawable);
    DrawableCompat.setTintList(wrappedDrawable,tint);
    return wrappedDrawable;
    }
}

Now I would use this helper class like this :

现在我要用这个助手类

    Drawable bd = VectorDrawableUtils.loadVectorDrawableWithTint(
                R.drawable.ic_dropdown, R.color.black,getContext());
        bd.setBounds(0, 0, textView.getMeasuredHeight(), textView.getMeasuredHeight());
        textView.setCompoundDrawablesWithIntrinsicBounds(null, null, bd, null);

It is important to use a Context of a View or Activity, not the Application context! Hope it will solve your problem, or help someone else. And if someone has a better and cleaner solution, I am interested to know as well.

重要的是使用视图或活动的上下文,而不是应用程序上下文!希望它能解决你的问题,或者帮助别人。如果有人有更好更干净的解决方案,我也很想知道。

#2


0  

MyTextView class:

MyTextView类:

public class MyTextView extends AppCompatTextView {

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

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

public MyTextView(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.MyTextView);

        int defaultWidthHeight = 0;
        int widthHeight = 0;
        Drawable drawableLeft = null;
        Drawable drawableStart = null;
        Drawable drawableRight = null;
        Drawable drawableEnd = null;
        Drawable drawableBottom = null;
        Drawable drawableTop = null;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            drawableLeft = attributeArray.getDrawable(R.styleable.MyTextView_drawableLeftCompatTextView);
            drawableStart = attributeArray.getDrawable(R.styleable.MyTextView_drawableStartCompatTextView);
            drawableRight = attributeArray.getDrawable(R.styleable.MyTextView_drawableRightCompatTextView);
            drawableEnd = attributeArray.getDrawable(R.styleable.MyTextView_drawableEndCompatTextView);
            drawableBottom = attributeArray.getDrawable(R.styleable.MyTextView_drawableBottomCompatTextView);
            drawableTop = attributeArray.getDrawable(R.styleable.MyTextView_drawableTopCompatTextView);
        } else {
            final int drawableLeftId = attributeArray.getResourceId(R.styleable.MyTextView_drawableLeftCompatTextView, -1);
            final int drawableStartId = attributeArray.getResourceId(R.styleable.MyTextView_drawableStartCompatTextView, -1);
            final int drawableRightId = attributeArray.getResourceId(R.styleable.MyTextView_drawableRightCompatTextView, -1);
            final int drawableEndId = attributeArray.getResourceId(R.styleable.MyTextView_drawableEndCompatTextView, -1);
            final int drawableBottomId = attributeArray.getResourceId(R.styleable.MyTextView_drawableBottomCompatTextView, -1);
            final int drawableTopId = attributeArray.getResourceId(R.styleable.MyTextView_drawableTopCompatTextView, -1);

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

        if(!attributeArray.hasValue(R.styleable.MyTextView_drawableWidthHeightCompatTextView)) {
            if (attributeArray.hasValue(R.styleable.MyTextView_drawableLeftCompatTextView)) {
                defaultWidthHeight = drawableLeft.getIntrinsicWidth();
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableStartCompatTextView)) {
                defaultWidthHeight = drawableStart.getIntrinsicWidth();
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableRightCompatTextView)) {
                defaultWidthHeight = drawableRight.getIntrinsicWidth();
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableEndCompatTextView)) {
                defaultWidthHeight = drawableEnd.getIntrinsicWidth();
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableBottomCompatTextView)) {
                defaultWidthHeight = drawableBottom.getIntrinsicWidth();
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableTopCompatTextView)) {
                defaultWidthHeight = drawableTop.getIntrinsicWidth();
            }

            widthHeight = attributeArray.getInt(R.styleable.MyTextView_drawableWidthHeightCompatTextView, defaultWidthHeight);
        } else
            widthHeight = attributeArray.getInt(R.styleable.MyTextView_drawableWidthHeightCompatTextView, defaultWidthHeight);

        if(attributeArray.hasValue(R.styleable.MyTextView_drawableColorCompatTextView)){
            ColorStateList tintColor = attributeArray.getColorStateList(R.styleable.MyTextView_drawableColorCompatTextView);
            if (attributeArray.hasValue(R.styleable.MyTextView_drawableLeftCompatTextView)) {
                //drawableLeft.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                DrawableCompat.setTintList(drawableLeft, tintColor);
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableStartCompatTextView)) {
                //drawableStart.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                DrawableCompat.setTintList(drawableStart, tintColor);
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableRightCompatTextView)) {
                //drawableRight.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                DrawableCompat.setTintList(drawableRight, tintColor);
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableEndCompatTextView)) {
                //drawableEnd.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                DrawableCompat.setTintList(drawableEnd, tintColor);
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableBottomCompatTextView)) {
                //drawableBottom.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                DrawableCompat.setTintList(drawableBottom, tintColor);
            } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableTopCompatTextView)) {
                //drawableTop.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                DrawableCompat.setTintList(drawableTop, tintColor);
            }
        }

        WrappedDrawable drawableLeftWrapped = new WrappedDrawable(drawableLeft);
        drawableLeftWrapped.setBounds(0, 0, widthHeight, widthHeight);
        WrappedDrawable drawableStartWrapped = new WrappedDrawable(drawableStart);
        drawableStartWrapped.setBounds(0, 0, widthHeight, widthHeight);
        WrappedDrawable drawableRightWrapped = new WrappedDrawable(drawableRight);
        drawableRightWrapped.setBounds(0, 0, widthHeight, widthHeight);
        WrappedDrawable drawableEndWrapped = new WrappedDrawable(drawableEnd);
        drawableEndWrapped.setBounds(0, 0, widthHeight, widthHeight);
        WrappedDrawable drawableBottomWrapped = new WrappedDrawable(drawableBottom);
        drawableBottomWrapped.setBounds(0, 0, widthHeight, widthHeight);
        WrappedDrawable drawableTopWrapped = new WrappedDrawable(drawableTop);
        drawableTopWrapped.setBounds(0, 0, widthHeight, widthHeight);

        setCompoundDrawablesWithIntrinsicBounds(drawableLeftWrapped, drawableTopWrapped, drawableRightWrapped, drawableBottomWrapped);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
            setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStartWrapped, drawableTopWrapped, drawableEndWrapped, drawableBottomWrapped);

        attributeArray.recycle();
    }
}

class WrappedDrawable extends Drawable {

    private final Drawable _drawable;
    protected Drawable getDrawable() {
        return _drawable;
    }

    public WrappedDrawable(Drawable drawable) {
        super();
        _drawable = drawable;
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        //update bounds to get correctly
        super.setBounds(left, top, right, bottom);
        Drawable drawable = getDrawable();
        if (drawable != null) {
            drawable.setBounds(left, top, right, bottom);
        }
    }

    @Override
    public void setAlpha(int alpha) {
        Drawable drawable = getDrawable();
        if (drawable != null) {
            drawable.setAlpha(alpha);
        }
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        Drawable drawable = getDrawable();
        if (drawable != null) {
            drawable.setColorFilter(colorFilter);
        }
    }

    @Override
    public int getOpacity() {
        Drawable drawable = getDrawable();
        return drawable != null
                ? drawable.getOpacity()
                : PixelFormat.UNKNOWN;
    }

    @Override
    public void draw(Canvas canvas) {
        Drawable drawable = getDrawable();
        if (drawable != null) {
            drawable.draw(canvas);
        }
    }

    @Override
    public int getIntrinsicWidth() {
        Drawable drawable = getDrawable();
        return drawable != null
                ? drawable.getBounds().width()
                : 0;
    }

    @Override
    public int getIntrinsicHeight() {
        Drawable drawable = getDrawable();
        return drawable != null ?
                drawable.getBounds().height()
                : 0;
    }
}
}

attrs.xml:

attrs.xml:

<declare-styleable name="MyTextView">
    <attr name="drawableColorCompatTextView" format="reference|color"/>
    <attr name="drawableWidthHeightCompatTextView" format="integer"/>
    <attr name="drawableLeftCompatTextView" format="reference"/>
    <attr name="drawableStartCompatTextView" format="reference"/>
    <attr name="drawableRightCompatTextView" format="reference"/>
    <attr name="drawableEndCompatTextView" format="reference"/>
    <attr name="drawableTopCompatTextView" format="reference"/>
    <attr name="drawableBottomCompatTextView" format="reference"/>
</declare-styleable>

Usage:

用法:

<com.packagename.MyTextView
    android:id="@+id/txtUserName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:drawableLeftCompatTextView="@drawable/ic_username"
    app:drawableStartCompatTextView="@drawable/ic_username"
    app:drawableWidthHeightCompatTextView="48"
    app:drawableColorCompatTextView="@color/blue" />

Note: The only issue here is an unmodified vector(drawableWidthHeightCompatTextView didn't use), which vector's width and height are 24,
Is not equal in size on the device, with resized vector(vector's width and height are 12 and drawableWidthHeightCompatTextView="24").

注意:这里唯一的问题是一个未修改的向量(drawableWidthHeightCompatTextView没有使用),该向量的宽度和高度为24,在设备上的大小不相等,且具有调整大小的向量(向量的宽度和高度为12,drawwidthheightcompattextview =“24”)。