21

For my custom view:

MyCustomView extends View 

I made a VectorDrawable

mMyVectorDrawable = VectorDrawableCompat.create(getContext().getResources(), R.drawable.ic_some_image, null);

I have set its bounds

mMyVectorDrawable.setBounds(0, 0, mMyVectorDrawable.getIntrinsicWidth(), mMyVectorDrawable.getIntrinsicHeight());

And I draw it on the Canvas

mMyVectorDrawable.draw(canvas);

I see the image at position 0,0

but how do I position it? How do I position the Rect, I thought the first two params of setBounds would be X and Y coordinates of where to start drawing but this just affects the size.

How can I position my vector drawable on a canvas?

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Ersen Osman
  • 7,067
  • 8
  • 47
  • 80
  • 3
    either `translate()` your `Canvas` before drawing or `setBounds(x,y, intrinsic_width + x, intrinsic_height + y)`, i'm not sure about the second way though... – pskink Jun 03 '16 at 17:58

3 Answers3

23

You can translate your canvas before drawing the drawable onto it.

mMyVectorDrawable.setBounds(0, 0, width, height);
canvas.translate(dx, dy);
mMyVectorDrawable.draw(canvas);

The values dx and dy specify the offset from the coordinates (0, 0) where the next drawable will be drawn.

If you want, you can also undo the translation afterwards:

canvas.translate(-dx, -dy);
Danilo Bargen
  • 18,626
  • 15
  • 91
  • 127
  • 10
    You can use canvas.save() before operations on canvas, and canvas.restore() after operations to not do "reverse" translation – Gilian Apr 26 '17 at 21:55
6

The question is a bit old but if it can help somebody, I have an answer based on this one.

The idea is to get the vector drawable using:

Drawable drawable = AppCompatDrawableManager.get().getDrawable(context, R.drawable.my_drawable);

Then we get the Bitmap from this Drawable directly with the size we want to keep the quality. Then, because we have a bitmap we can draw it where we want.

Note: In the defaultConfig section of your build.gradle, don't forget to put this line for retro-compatibility:

vectorDrawables.useSupportLibrary = true

Here is the code to get the Bitmap from the Drawable:

/**
 * Extract the Bitmap from a Drawable and resize it to the expectedSize conserving the ratio.
 *
 * @param drawable   Drawable used to extract the Bitmap. Can be null.
 * @param expectSize Expected size for the Bitmap. Use {@link #DEFAULT_DRAWABLE_SIZE} to
 *                   keep the original {@link Drawable} size.
 * @return The Bitmap associated to the Drawable or null if the drawable was null.
 * @see <html><a href="https://stackoverflow.com/a/10600736/1827254">Stackoverflow answer</a></html>
 */
public static Bitmap getBitmapFromDrawable(@Nullable Drawable drawable, int expectSize) {
    Bitmap bitmap;

    if (drawable == null) {
        return null;
    }

    if (drawable instanceof BitmapDrawable) {
        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
        if (bitmapDrawable.getBitmap() != null) {
            return bitmapDrawable.getBitmap();
        }
    }

    if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
        bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
    } else {
        float ratio = (expectSize != DEFAULT_DRAWABLE_SIZE)
                ? calculateRatio(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), expectSize)
                : 1f;

        int width = (int) (drawable.getIntrinsicWidth() * ratio);
        int height = (int) (drawable.getIntrinsicHeight() * ratio);

        bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    }

    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);

    return bitmap;
}

/**
 * Calculate the ratio to multiply the Bitmap size with, for it to be the maximum size of
 * "expected".
 *
 * @param height   Original Bitmap height
 * @param width    Original Bitmap width
 * @param expected Expected maximum size.
 * @return If height and with equals 0, 1 is return. Otherwise the ratio is returned.
 * The ration is base on the greatest side so the image will always be the maximum size.
 */
public static float calculateRatio(int height, int width, int expected) {
    if (height == 0 && width == 0) {
        return 1f;
    }
    return (height > width)
            ? expected / (float) width
            : expected / (float) height;
}
Community
  • 1
  • 1
Eselfar
  • 3,759
  • 3
  • 23
  • 43
  • 1
    As of (at least) support lib `26.0.2` (and probably earlier?) it'll complain that you're trying to use a non-public API here, when trying to use `AppCompatDrawableManager`. I think the correct alternative is to go with `ContextCompat.getDrawable(context, id)` – Ionoclast Brigham Nov 23 '17 at 01:09
  • I was using the version `25.3.1` of the support library when I wrote this answer. But you're right, I tried with a more recent version (`'26.0.2'`) and it seems that you can't use this class anymore. It's not even referenced in [the documentation](https://developer.android.com/reference/android/support/v7/widget/package-summary.html). Does it work with your solution? (`ContextCompat.getDrawable(context, id)`) – Eselfar Nov 23 '17 at 09:34
  • 1
    Yes. It's what I'm using when I rasterize vector assets. – Ionoclast Brigham Nov 27 '17 at 19:51
5

As @pskink said, this works fine:

setBounds(x, y, intrinsic_width + x, intrinsic_height + y)
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
mackerel
  • 131
  • 2
  • 7