Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set the size of vector drawable inside of a button in Android?

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:

Converted Vector to PNG

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

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

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.

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

<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:

The result of some tweaking

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.

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.

like image 428
Valkova.V Avatar asked Nov 13 '15 10:11

Valkova.V


1 Answers

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

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.

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.

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

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.

like image 122
Ivelius Avatar answered Sep 21 '22 12:09

Ivelius