How to make any view to draw to canvas?

47,797

Yeah, you can do this. Keep in mind, since you're not attaching it to a layout, you'll need to lay it out manually before drawing it:

view.layout(0, 0, viewWidth, viewHeight);

And unless you just know exactly what you want for those width and height parameters, you may also want to measure it first:

int widthSpec = MeasureSpec.makeMeasureSpec (ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.UNSPECIFIED;
int heightSpec = MeasureSpec.makeMeasureSpec (400, MeasureSpec.UNSPECIFIED;
view.measure(widthSpec, heightSpec);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());

EDIT:

If you already know the width and height you need:

//Lay the view out with the known dimensions
view.layout (0, 0, rect.width(), rect.height());

//Translate the canvas so the view is drawn at the proper coordinates
canvas.save();
canvas.translate(rect.left, rect.top);

//Draw the View and clear the translation
view.draw(canvas);
canvas.restore();

EDIT again:

Yes, tested. You can try this yourself:

public class DrawingActivity extends Activity {
    public void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //Set a Rect for the 200 x 200 px center of a 400 x 400 px area
        Rect rect = new Rect();
        rect.set(100, 100, 300, 300);

        //Allocate a new Bitmap at 400 x 400 px
        Bitmap bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);

        //Make a new view and lay it out at the desired Rect dimensions
        TextView view = new TextView(this);
        view.setText("This is a custom drawn textview");
        view.setBackgroundColor(Color.RED);
        view.setGravity(Gravity.CENTER);

        //Measure the view at the exact dimensions (otherwise the text won't center correctly)
        int widthSpec = View.MeasureSpec.makeMeasureSpec(rect.width(), View.MeasureSpec.EXACTLY);
        int heightSpec = View.MeasureSpec.makeMeasureSpec(rect.height(), View.MeasureSpec.EXACTLY);
        view.measure(widthSpec, heightSpec);

        //Lay the view out at the rect width and height
        view.layout(0, 0, rect.width(), rect.height());

        //Translate the Canvas into position and draw it
        canvas.save();
        canvas.translate(rect.left, rect.top);
        view.draw(canvas);
        canvas.restore();

        //To make sure it works, set the bitmap to an ImageView
        ImageView imageView = new ImageView(this);
        imageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        setContentView(imageView);
        imageView.setScaleType(ImageView.ScaleType.CENTER);
        imageView.setImageBitmap(bitmap);
    }
}
Share:
47,797
android developer
Author by

android developer

Really like to develop Android apps & libraries on my spare time. Github website: https://github.com/AndroidDeveloperLB/ My spare time apps: https://play.google.com/store/apps/developer?id=AndroidDeveloperLB

Updated on July 09, 2022

Comments

  • android developer
    android developer almost 2 years

    I have a short question:

    Suppose I have a (mutable) bitmap that I need to modify (add images, texts, etc...) .

    Instead of messing around with many special classes for drawing to the canvas (paint, canvas, matrices and so on), I was thinking why not use the built in classes of Android for this task and only if i need really customized operations i could still use the canvas ?

    So, for example, in order to show any kind of view (that has no parent, of course) on the bitmap, I could call the next function :

    public void drawViewToBitmap(Bitmap b, View v, Rect rect) {
        Canvas c = new Canvas(b);
        // <= use rect to let the view to draw only into this boundary inside the bitmap
        view.draw(c);
    }
    

    Is such a thing possible? maybe that's even the way that it works behind the scenes?

    what should i write in the part between the drawing and the creation of the canvas?


    EDIT: i've tried the next code , but it didn't work:

    public void drawFromViewToCanvas(final View view, final Rect rect, final Canvas canvas) {
        final int widthSpec = View.MeasureSpec.makeMeasureSpec(rect.width(), View.MeasureSpec.EXACTLY);
        final int heightSpec = View.MeasureSpec.makeMeasureSpec(rect.height(), View.MeasureSpec.EXACTLY);
        view.measure(widthSpec, heightSpec);
        // Lay the view out with the known dimensions
        view.layout(0, 0, rect.width(), rect.height());
        // Translate the canvas so the view is drawn at the proper coordinates
        canvas.save();
        canvas.translate(rect.left, rect.top);
        // Draw the View and clear the translation
        view.draw(canvas);
        canvas.restore();
    }
    

    example of usage:

    final int imageSize = 50;
    rect = new Rect(35, 344 , 35 + imageSize, 344  + imageSize);
    final ImageView imageView = new ImageView(mContext);
    imageView.setImageBitmap(bitmap);
    imageView.setScaleType(ScaleType.CENTER_CROP);
    drawFromViewToCanvas(imageView, getRect(), canvas);
    

    EDIT: there is a sample on sony's website:

    int measureWidth = View.MeasureSpec.makeMeasureSpec(bitmapWidth, View.MeasureSpec.EXACTLY);
    int measuredHeight = View.MeasureSpec.makeMeasureSpec(bitmapHeight, View.MeasureSpec.EXACTLY);
    view.measure(measureWidth, measuredHeight);
    view.layout(0, 0, bitmapWidth, bitmapHeight);
    view.draw(canvas);
    

    wonder if it works.

  • android developer
    android developer almost 11 years
    the rect i've mentioned should be used for the width and height that the view is allowed to draw to. can i use its width and height? also, where is the part that draws to the location of the rect?
  • Kevin Coppock
    Kevin Coppock almost 11 years
    If you already know the location and size, it's much simpler. See the edit.
  • android developer
    android developer almost 11 years
    are you sure it works? have you tried it , for example , on a textView or an imageView ?
  • android developer
    android developer almost 11 years
    cool. will test it soon and mark this answer afterwards. thank you! can you also check out this very similar question: stackoverflow.com/questions/17189809/… , or maybe it's the exact same?
  • Kevin Coppock
    Kevin Coppock almost 11 years
    Sounds like the same question to me.
  • android developer
    android developer almost 11 years
    so there, i would inflate the xml layout, and then use this code on the root view that was created by it?
  • android developer
    android developer almost 11 years
    your solution didn't work, as the views have drawn content outside of the rectangle. for example, i've tried using an imageView with ScaleType.CENTER_CROP and rectangle that has width=50 and height=50 , yet the image is 100x50 , and it has drawn outside of its rectangle.
  • Kevin Coppock
    Kevin Coppock almost 11 years
    Sounds like you didn't measure it to be 50x50.
  • android developer
    android developer almost 11 years
    i thought your middle code is the correct one. you mean it is only a part of a solution? please edit your answer to have the correct solution, including all the lines needed .
  • Kevin Coppock
    Kevin Coppock almost 11 years
    My answer has a proper working example. The rest is an exercise for yourself.
  • android developer
    android developer almost 11 years
    still doesn't work. i've updated my question to have the code i'm using. please check it out.
  • box
    box almost 7 years
    I tried drawing a ProgressBar the same way, which is working on Lollipop, but it is not working on Android 7.0 Nougat? The drawing of ProgressBar works on previous versions, but on Nougat SDK version the ProgressBar drawing on canvas is not working.
  • Skullper
    Skullper almost 3 years
    Great answer! Gave me a glimpse of hope in solving my issue!