156

I will try to explain what exactly I need to do.

I have 3 separate screens say A,B,C. There is another screen called say HomeScreen where all the 3 screens bitmap should be displayed in Gallery view and the user can select in which view does he wants to go.

I have been able to get the Bitmaps of all the 3 screens and display it in Gallery view by placing all the code in HomeScreen Activity only. Now, this has complicated the code a lot and I will like to simplify it.

So, can I call another Activity from HomeScreen and do not display it and just get the Bitmap of that screen. For example, say I just call HomeScreen and it calls Activity A,B,C and none of the Activities from A,B,C are displayed. It just gives the Bitmap of that screen by getDrawingCache(). And then we can display those bitmaps in Gallery view in HomeScreen.

I hope I have explained the problem very clearly.

Please let me know if this is actually possible.

Raghav Sood
  • 81,899
  • 22
  • 187
  • 195
sunil
  • 9,541
  • 18
  • 66
  • 88
  • 6
    Actually, I was able to do this. – sunil Mar 23 '11 at 11:38
  • 1
    I'm not entirely sure, but I think you won't be able to do that. The problem is that activities are meant to be displayed to the user. You can start the activity and then immediately hide it, but the activity will still be visible to the user for a split-second. It's shown long enough to be noticed so having the screen flicker several times makes the app look unprofessional. However, it might be possible that there is a command to start an activity without displaying it; I just don't know of one if it exists. – Steve Haley May 10 '10 at 09:45
  • Oh,how can you call that activity but not to show it?Can I take the current activity's layout as the template to generate bitmap while feeding different content to it? – zionpi Jul 09 '15 at 14:29
  • Check answer in this post, i found some kind of solution: http://stackoverflow.com/questions/36424381/save-multiple-textviews-as-image-of-large-resolution/36455437#36455437 – Wackaloon Apr 07 '16 at 06:13
  • nothing worked for me from above answers. this worked only https://stackoverflow.com/a/26086145/8554111 – sum20156 Nov 29 '21 at 09:05

13 Answers13

239

there is a way to do this. you have to create a Bitmap and a Canvas and call view.draw(canvas);

here is the code:

public static Bitmap loadBitmapFromView(View v) {
    Bitmap b = Bitmap.createBitmap( v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_8888);                
    Canvas c = new Canvas(b);
    v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
    v.draw(c);
    return b;
}

if the view wasn't displayed before the size of it will be zero. Its possible to measure it like this:

if (v.getMeasuredHeight() <= 0) {
    v.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    Bitmap b = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);
    v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
    v.draw(c);
    return b;
}

EDIT: according to this post, Passing WRAP_CONTENT as value to makeMeasureSpec() doesn't to do any good (although for some view classes it does work), and the recommended method is:

// Either this
int specWidth = MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.AT_MOST);
// Or this
int specWidth = MeasureSpec.makeMeasureSpec(0 /* any */, MeasureSpec.UNSPECIFIED);
view.measure(specWidth, specWidth);
int questionWidth = view.getMeasuredWidth();
Community
  • 1
  • 1
Simon
  • 13,173
  • 14
  • 66
  • 90
  • 1
    I tried this but all I get is a semi transparent black box. Do I need to do something on the view to get it ready for bitmap drawing? – Bobbake4 Oct 14 '11 at 19:02
  • 4
    I actually had to change this to `v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());` to get this to work correctly, but thanks for the code :) – triad Aug 17 '12 at 19:06
  • 4
    I had to use v.getWidth() instead of v.getLayoutParams().width and similar for height. Otherwise, now working. – David Manpearl Sep 03 '12 at 05:55
  • 1
    I used ````v.measure(0, 0); v.getMeasuredWidth(); v.getMeasuredHeight();````. – Brais Gabin Jan 18 '13 at 17:55
  • As soon as I call v.measure, my app crashes (view is non-null). – levigroker Oct 18 '13 at 16:19
  • How do I use it in my Main Activity class? – Si8 Feb 06 '14 at 00:44
  • 7
    `Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);` Works better – Pierre Feb 09 '14 at 14:50
  • i got this exception 10-26 16:47:23.807: E/AndroidRuntime(24169): java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@425a9728 – Muhammad Babar Oct 26 '14 at 11:52
  • This solution will not work in this case: The View, you are trying to convert, is a layout file and its outermost layout has fixed size and its background image (if set) is larger than the layout size. In this case, it will generate a bitmap of size of background image. So in my case, I added on more outer layout and set its size to `WRAP_CONTENT`, and it generated the perfect bitmap. – Chintan Shah Dec 18 '15 at 09:16
  • I just uses v.getMeasuredWidth() & v.getMeasuredHeight(). In case of v.height & v.width it was getting -1. – CoDe Dec 06 '16 at 13:19
  • It's not working on TextView, only draw the background, text not draw on the canvas. Any one know how to solve it? – aiueoH Dec 06 '18 at 02:17
  • I couldn't get this to work from a textview. But I could render the text directly using a Canvas.drawText() call. – Brent K. Nov 03 '21 at 15:55
36

here is my solution:

public static Bitmap getBitmapFromView(View view) {
    Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(returnedBitmap);
    Drawable bgDrawable =view.getBackground();
    if (bgDrawable!=null) 
        bgDrawable.draw(canvas);
    else 
        canvas.drawColor(Color.WHITE);
    view.draw(canvas);
    return returnedBitmap;
}

Enjoy :)

Gil SH
  • 3,789
  • 1
  • 27
  • 25
  • Thanks. I was having problems on some devices if the height was beyond a certain value. I have not tested fully but this appears to resolve that. – B-M-F Nov 13 '13 at 00:58
30

I know this may be a stale issue, but I was having problems getting any of these solutions to work for me. Specifically, I found that if any changes were made to the view after it was inflated that those changes would not get incorporated into the rendered bitmap.

Here's the method which ended up working for my case. With one caveat, however. prior to calling getViewBitmap(View) I inflated my view and asked it to layout with known dimensions. This was needed since my view layout would make it zero height/width until content was placed inside.

View view = LayoutInflater.from(context).inflate(layoutID, null);
//Do some stuff to the view, like add an ImageView, etc.
view.layout(0, 0, width, height);

Bitmap getViewBitmap(View view)
{
    //Get the dimensions of the view so we can re-layout the view at its current size
    //and create a bitmap of the same size 
    int width = view.getWidth();
    int height = view.getHeight();

    int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
    int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);

    //Cause the view to re-layout
    view.measure(measuredWidth, measuredHeight);
    view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());

    //Create a bitmap backed Canvas to draw the view into
    Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);

    //Now that the view is laid out and we have a canvas, ask the view to draw itself into the canvas
    view.draw(c);

    return b;
}

The "magic sauce" for me was found here: https://groups.google.com/forum/#!topic/android-developers/BxIBAOeTA1Q

Cheers,

Levi

levigroker
  • 2,087
  • 1
  • 21
  • 30
  • Cheers! It seems that one must call measure and requestLayout after any changes to the layout for them to be displayed – TheIT Jan 07 '19 at 04:32
  • 1
    Thanks for this solution! I had the same problem. I was using `measure()` and `layout()` before I populated my view so I had strange results. Moving these calls down, above `createBitmap()` fixed it for me! – Sven Jacobs Jan 24 '19 at 13:14
24

Try this,

/**
 * Draw the view into a bitmap.
 */
public static Bitmap getViewBitmap(View v) {
    v.clearFocus();
    v.setPressed(false);

    boolean willNotCache = v.willNotCacheDrawing();
    v.setWillNotCacheDrawing(false);

    // Reset the drawing cache background color to fully transparent
    // for the duration of this operation
    int color = v.getDrawingCacheBackgroundColor();
    v.setDrawingCacheBackgroundColor(0);

    if (color != 0) {
        v.destroyDrawingCache();
    }
    v.buildDrawingCache();
    Bitmap cacheBitmap = v.getDrawingCache();
    if (cacheBitmap == null) {
        Log.e(TAG, "failed getViewBitmap(" + v + ")", new RuntimeException());
        return null;
    }

    Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);

    // Restore the view
    v.destroyDrawingCache();
    v.setWillNotCacheDrawing(willNotCache);
    v.setDrawingCacheBackgroundColor(color);

    return bitmap;
}
aldok
  • 17,295
  • 5
  • 53
  • 64
Ashish Dwivedi
  • 8,048
  • 5
  • 58
  • 78
21

There is a great Kotlin extension function in Android KTX: View.drawToBitmap(Bitmap.Config)

a.ch.
  • 8,285
  • 5
  • 40
  • 53
  • 19
    This will not work if the view is not presented in the layout. Error: "IllegalStateException: View needs to be laid out before calling drawToBitmap()" – Val Apr 20 '20 at 05:55
  • Easy and simple. Thanks brother – Nabeel Jun 29 '21 at 11:55
  • 1
    I does work if you call `myView.layout(myView.left, myView.top, myView.measuredWidth, myView.measuredHeight)` be sure to call `myView.measure(MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY))` before that – FlavienSi Apr 28 '22 at 15:40
  • @FlavienSi Always black background for circle view. Do you know way to change it ? – billy gates Nov 29 '22 at 15:58
5

Layout or view to bitmap:

 private Bitmap createBitmapFromLayout(View tv) {      
    int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    tv.measure(spec, spec);
    tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());
    Bitmap b = Bitmap.createBitmap(tv.getMeasuredWidth(), tv.getMeasuredWidth(),
            Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);
    c.translate((-tv.getScrollX()), (-tv.getScrollY()));
    tv.draw(c);
    return b;
}

Calling Method:

Bitmap src = createBitmapFromLayout(View.inflate(this, R.layout.sample, null)/* or pass your view object*/);
Shyam Kumar
  • 909
  • 11
  • 12
4

I think this is a bit better :

/**
 * draws the view's content to a bitmap. code initially based on :
 * http://nadavfima.com/android-snippet-inflate-a-layout-draw-to-a-bitmap/
 */
@Nullable
public static Bitmap drawToBitmap(final View viewToDrawFrom, int width, int height) {
    boolean wasDrawingCacheEnabled = viewToDrawFrom.isDrawingCacheEnabled();
    if (!wasDrawingCacheEnabled)
        viewToDrawFrom.setDrawingCacheEnabled(true);
    if (width <= 0 || height <= 0) {
        if (viewToDrawFrom.getWidth() <= 0 || viewToDrawFrom.getHeight() <= 0) {
            viewToDrawFrom.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            width = viewToDrawFrom.getMeasuredWidth();
            height = viewToDrawFrom.getMeasuredHeight();
        }
        if (width <= 0 || height <= 0) {
            final Bitmap bmp = viewToDrawFrom.getDrawingCache();
            final Bitmap result = bmp == null ? null : Bitmap.createBitmap(bmp);
            if (!wasDrawingCacheEnabled)
                viewToDrawFrom.setDrawingCacheEnabled(false);
            return result;
        }
        viewToDrawFrom.layout(0, 0, width, height);
    } else {
        viewToDrawFrom.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
        viewToDrawFrom.layout(0, 0, viewToDrawFrom.getMeasuredWidth(), viewToDrawFrom.getMeasuredHeight());
    }
    final Bitmap drawingCache = viewToDrawFrom.getDrawingCache();
    final Bitmap bmp = ThumbnailUtils.extractThumbnail(drawingCache, width, height);
    final Bitmap result = bmp == null || bmp != drawingCache ? bmp : Bitmap.createBitmap(bmp);
    if (!wasDrawingCacheEnabled)
        viewToDrawFrom.setDrawingCacheEnabled(false);
    return result;
}

Using the above code, you don't have to specify the size of the bitmap (use 0 for width&height) if you want to use the one of the view itself.

Also, if you wish to convert special views (SurfaceView, Surface or Window, for example) to a bitmap, you should consider using PixelCopy class instead. It requires API 24 and above though. I don't know how to do it before.

android developer
  • 114,585
  • 152
  • 739
  • 1,270
4

I had a similar need and developed something based on codes provided, specially the one by @Azhagthott and the brand new androidx.core.view.drawToBitmap, hopefully this will be hopeful here also,

import androidx.core.view.drawToBitmap

val view = MyCustomView(context)
view.measure(
    View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST),
    View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST)
)
view.layout(0, 0, width, height)
val bitmap = view.drawToBitmap()
Ebrahim Byagowi
  • 10,338
  • 4
  • 70
  • 81
2

Hope this helps

View view="some view instance";        
view.setDrawingCacheEnabled(true);
Bitmap bitmap=view.getDrawingCache();
view.setDrawingCacheEnabled(false);

Update
getDrawingCache() method is deprecated in API level 28. So look for other alternative for API level > 28.

Akhilesh Kumar
  • 796
  • 1
  • 5
  • 12
1

I used this just a few days ago:

fun generateBitmapFromView(view: View): Bitmap {
    val specWidth = View.MeasureSpec.makeMeasureSpec(1324, View.MeasureSpec.AT_MOST)
    val specHeight = View.MeasureSpec.makeMeasureSpec(521, View.MeasureSpec.AT_MOST)
    view.measure(specWidth, specHeight)
    val width = view.measuredWidth
    val height = view.measuredHeight
    val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bitmap)
    view.layout(view.left, view.top, view.right, view.bottom)
    view.draw(canvas)
    return bitmap
}

This code is based in this gist

Azhagthott
  • 492
  • 5
  • 13
0
private fun getBitmapFromView(view: View): Bitmap
{
    val returnedBitmap = Bitmap.createBitmap(view.width,
        view.height
    ,Bitmap.Config.ARGB_8888)

    val canvas = Canvas(returnedBitmap)

    //background image
    val bgDrawable = view.background

    //background image is selected
    if (bgDrawable != null){
        bgDrawable.draw(canvas)
    }
    else{
        canvas.drawColor(Color.WHITE)
    }
    
    view.draw(canvas)
    
    return returnedBitmap

}
0

I had the same problem in my project and found a solution, which I will describe here.

public Bitmap catchBitmapFromView(View capture_view){
        if (capture_view == null) {
            return null;
        }

        capture_view.setDrawingCacheEnabled(true);

        Bitmap bitmap = Bitmap.createBitmap(capture_view.getWidth(), capture_view.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Drawable bgDrawable = capture_view.getBackground();
        if (bgDrawable != null) {
            bgDrawable.draw(canvas);
        } else {
            canvas.drawColor(Color.WHITE);
        }
        capture_view.draw(canvas);

        capture_view.setDrawingCacheEnabled(false);
        return bitmap;
}
Urvish Shiroya
  • 530
  • 1
  • 7
-3
view.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);