0

My problem is similar to the one in Android View.getDrawingCache returns null, only null.

My code used to work with the method

private Bitmap getBitmapFromView(View v) {
    v.setDrawingCacheEnabled(true);
    v.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
    v.buildDrawingCache(true);
    Bitmap b = Bitmap.createBitmap(v.getDrawingCache());
    v.setDrawingCacheEnabled(false); // clear drawing cache
    return b;
}

But today I changed the xml layout file. Since the change I have been getting NullPointerException at the line Bitmap b = Bitmap.createBitmap(v.getDrawingCache()). So I tried changing the method getBitmapFromView to

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(0, 0, v.getLayoutParams().width, v.getLayoutParams().height);
    v.draw(c);
    return b;
}

But that didn't help. I get the null in the very first line.

The situation is this: I am trying to convert the view into a bitmap. But I am doing this without first displaying the view on screen. Again, this all used to work until I changed the content of the xml layout file. The change was not even that big. I just moved stuff around and removed -- yes removed -- some of the subviews.

In any case, I know for certain there is no problem with my layout file because I can display it on screen if I want; which I have done as part of troubleshooting.

Someone suggested using a handler to call getDrawingCache(). If that's the answer, how would I write that code?

UPDATE

View view = activity.getLayoutInflater().inflate(R.layout.my_view, null, false);
    findViewsByIds(view);//where I inflate subviews and add content to them.
Community
  • 1
  • 1
Katedral Pillon
  • 14,534
  • 25
  • 99
  • 199
  • In View v, you are setting drawable from resource ? – Yogendra Mar 16 '15 at 19:16
  • @Yogendra I have updated to show you how I am getting `view` which is also the `v` you are referring. The contents are texts and images. One of the images is a drawable resource but that is set inside the xml file. The others are added after I inflate the subviews. Again, none of the contents have been changed between this new layout and the old layout. It's the exact same content that I am adding. – Katedral Pillon Mar 16 '15 at 19:21
  • Can you post the layout XML before and after the change? – Elhanan Mishraky Mar 16 '15 at 19:28

2 Answers2

1

Since you are not showing the view it's easier to just draw the view on a canvas. You are already trying to do that but I would merge both methods into one without using drawing cache.

private Bitmap getBitmapFromView(View v) {
    v.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredWidth());
    Bitmap b = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredWidth(), Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);
    v.draw(c);
    return b;
}

Also check that MeasureSpec.UNSPECIFIED may not work well without the view in layout so you may need to specify a width and height.

Marcio Covre
  • 4,556
  • 2
  • 22
  • 24
0

Execute createBitmapFromView when the view is measured.

public static void executeWhenViewMeasured(final View view, final Runnable runnable) {
    if(view.getMeasuredHeight() == 0 || view.getMeasuredWidth() == 0){
        view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                runnable.run();
                removeGlobalOnLayoutListener(view, this);
            }
        });
    } else {
        runnable.run();
    }
}

public static Bitmap createBitmapFromView(View view) {
    Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    view.draw(canvas);
    return bitmap;
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public static void removeGlobalOnLayoutListener(View view,
                                                ViewTreeObserver.OnGlobalLayoutListener listener) {
    try {
        view.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
    } catch (NoSuchMethodError e) {
        view.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
    }
}

executeWhenViewMeasured(view, new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = createBitmapFromView(view);
            }
        });
Semyon Tikhonenko
  • 3,872
  • 6
  • 36
  • 61
  • I never get inside `view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()`. Could it be because the class from which I am inflating the image is not an activity or a fragment, but rather just a "normal" java class? (again, it used to work before, so I don't know if that matters) – Katedral Pillon Mar 16 '15 at 19:55
  • Also I am never showing the view on screen. I am inflating it for the sole purpose of converting it to a bitmap. Maybe that is why the call is never made. – Katedral Pillon Mar 16 '15 at 20:13