4

I'm trying to create and render a view completely offscreen. First, I've tried (from http://arpitonline.com/2012/07/17/capturing-bitmaps-of-views-in-android/):

Bitmap result = Bitmap.createBitmap(1080, 1080, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(result);
view.layout(0, 0, 1080, 1080);
view.draw(c);
return result;

It rendered only my view's background color. It's the correct size and correct background color, but the contents are completely empty. Then I've stumbled upon this:

view.layout(0, 0, 1080, 1080);
view.invalidate();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bmp = view.getDrawingCache();
Bitmap result = bmp.copy(Bitmap.Config.ARGB_8888, false);
view.destroyDrawingCache();
return result;

But again, it yields the same result: correct size and background color with empty contents. There are many views inside my view, I set many properties on them, there are no null pointers etc, everything gets set correctly. But they are just not drawn. Why would this happen?

UPDATE: As psking suggested, I've moved my drawing logic to View#post but it's still the same (though it takes longer to fire the completion, which may be a clue to the view actually doing something). Tried both methods:

(completion is my function that takes a Bitmap)

final Bitmap result = Bitmap.createBitmap(1080, 1080, Bitmap.Config.ARGB_8888);
final Canvas c = new Canvas(result);
view.layout(0, 0, 1080, 1080);
view.post(new Runnable() {
    @Override
    public void run() {
        view.draw(c);
        completion.call(result);
    }
});

and

view.layout(0, 0, 1080, 1080);
view.invalidate();
view.setDrawingCacheEnabled(true);
view.post(new Runnable() {
    @Override
    public void run() {
        view.buildDrawingCache();
        Bitmap bmp = view.getDrawingCache();
        Bitmap result = bmp.copy(Bitmap.Config.ARGB_8888, false);
        view.destroyDrawingCache();
        completion.call(result);
    }
});

But still the same.

Can Poyrazoğlu
  • 33,241
  • 48
  • 191
  • 389
  • tried dispatchDraw(c) ? i know it's protected... also if your view is a `ViewGroup` you cannot call `draw` just after `layout` call, you need the view to be "laid out" actually – pskink Aug 27 '15 at 07:50
  • @pskink the default implementation in View (which is my case) does nothing: `/** * Called by draw to draw the child views. This may be overridden * by derived classes to gain control just before its children are drawn * (but after its own view has been drawn). * @param canvas the canvas on which to draw the view */ protected void dispatchDraw(Canvas canvas) { }` – Can Poyrazoğlu Aug 27 '15 at 07:52
  • call `draw` / `dispatchDraw` in a `Runnable` that is passed to `View#post(Runnable)` to make sure your view is "laid out" – pskink Aug 27 '15 at 07:58
  • @pskink tried (with both methods). no avail. see my updated question. – Can Poyrazoğlu Aug 27 '15 at 08:18

2 Answers2

8
int sizePixels = 1080;

Bitmap result = Bitmap.createBitmap(sizePixels, sizePixels, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(result);

// measure view first
int sizeSpec = View.MeasureSpec.makeMeasureSpec(sizePixels, View.MeasureSpec.EXACTLY);
view.measure(sizeSpec, sizeSpec);

// then layout
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
view.layout(0, 0, width, height);

// now you can draw it
view.draw(c);
sergej shafarenka
  • 20,071
  • 7
  • 67
  • 86
0

I think it is because even though you have called layout, the view is not actually laid out in relation to the view tree until later. The onGlobalLayoutChangedListener lets you listen to that event.

Try this process:

  1. Inflate the view to the existing viewtree. (You could have a framelayout that is usually hidden within another layout.

  2. Add an onGlobalLayoutChangedListener to the view, measure the view and grab the bitmap.

    ViewTreeObserver vto = view.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.RGB_565); Canvas canvas = new Canvas(bitmap); canvas.drawColor(Color.WHITE); view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); view.draw(canvas); }};

Edit: Sorry about the formatting, the indent tool doesn't seem to be working for me now

reden
  • 968
  • 7
  • 14