42

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.

Ziem
  • 6,579
  • 8
  • 53
  • 86
android developer
  • 114,585
  • 152
  • 739
  • 1,270

1 Answers1

70

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);
    }
}
Kevin Coppock
  • 133,643
  • 45
  • 263
  • 274
  • 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? – android developer Jul 08 '13 at 17:33
  • If you already know the location and size, it's much simpler. See the edit. – Kevin Coppock Jul 08 '13 at 17:47
  • are you sure it works? have you tried it , for example , on a textView or an imageView ? – android developer Jul 08 '13 at 17:58
  • cool. will test it soon and mark this answer afterwards. thank you! can you also check out this very similar question: http://stackoverflow.com/questions/17189809/is-it-possible-to-take-a-screenshot-of-a-view-without-showing-the-view , or maybe it's the exact same? – android developer Jul 08 '13 at 19:51
  • Sounds like the same question to me. – Kevin Coppock Jul 08 '13 at 19:58
  • so there, i would inflate the xml layout, and then use this code on the root view that was created by it? – android developer Jul 08 '13 at 22:38
  • 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. – android developer Jul 09 '13 at 06:50
  • Sounds like you didn't measure it to be 50x50. – Kevin Coppock Jul 09 '13 at 16:37
  • 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 . – android developer Jul 09 '13 at 18:19
  • My answer has a proper working example. The rest is an exercise for yourself. – Kevin Coppock Jul 09 '13 at 18:23
  • still doesn't work. i've updated my question to have the code i'm using. please check it out. – android developer Jul 10 '13 at 07:15
  • 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. – box May 16 '17 at 11:09
  • Great answer! Gave me a glimpse of hope in solving my issue! – Skullper Jun 02 '21 at 10:58