How to convert view to Bitmap with shadow in Android L - android

I want to convert a ScrollView to Bitmap and I found the solution below.
private Bitmap getBitmapFromView(View v) {
// store the origin state
int originWidth = v.getMeasuredWidth();
int originHeight = v.getMeasuredHeight();
// capture the view
int specWidth = View.MeasureSpec.makeMeasureSpec(originWidth, View.MeasureSpec.EXACTLY);
int specHeight = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
v.measure(specWidth, specHeight);
Bitmap b = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
Canvas c = new Canvas(b);
c.drawColor(getResources().getColor(R.color.default_background));
v.draw(c);
// restore the view
v.measure(originWidth, originHeight);
v.layout(0, 0, originWidth, originHeight);
return b;
}
It works fine except the shadow, as there are CardView in the layout, the method loses all the shadows, it makes the image a little strange.
So are there any ways to convert without losing shadows?

Related

Can not get Bitmap from LinearLayout in Android

I tried the following code to convert the LinearLayout to image:
public static Bitmap loadBitmapFromView(View v) {
v.measure(
View.MeasureSpec.makeMeasureSpec(v.getLayoutParams().width, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(v.getLayoutParams().height, View.MeasureSpec.EXACTLY));
v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight()
, Bitmap.Config.RGB_565);
Canvas c = new Canvas(b);
v.draw(c);
return b;
}
but I have this exception
java.lang.IllegalArgumentException: bitmap size exceeds 32 bits
How I can solve this problem?
Use Bitmap.Config.ARGB_8888 and make sure the resolved width and height are not 0.
Alternatively you can also use getDrawingCache
view.setDrawingCacheEnabled(true);
view.buildDrawingCache(true);
Bitmap bitmap = view.getDrawingCache();
Try this:
private Bitmap generateBitmap(LinearLayout view)
{
//Provide it with a layout params. It should necessarily be wrapping the
//content as we not really going to have a parent for it.
view.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
//Pre-measure the view so that height and width don't remain null.
view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
//Assign a size and position to the view and all of its descendants
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
//Create the bitmap
Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(),
view.getMeasuredHeight(),
Bitmap.Config.ARGB_8888);
//Create a canvas with the specified bitmap to draw into
Canvas c = new Canvas(bitmap);
//Render this view (and all of its children) to the given Canvas
view.draw(c);
return bitmap;
}
public static Bitmap loadBitmapFromView(View view) {
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
return view.getDrawingCache();
}
Try this code to get Bitmap from LinearLayout in Android:
public static Bitmap CreateBitmap(View layout) {
layout.setDrawingCacheEnabled(true);
layout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
layout.layout(0, 0, layout.getMeasuredWidth(), layout.getMeasuredHeight());
layout.buildDrawingCache(true);
Bitmap bitmap = Bitmap.createBitmap(layout.getWidth(), layout.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
layout.draw(canvas);
return bitmap;
}

Android Create Bitmap from RelativeLayout ChildView not scalling properly

Here is the code for convert view to bitmap.
public static Bitmap loadBitmapFromView(View v) {
v.measure(View.MeasureSpec.makeMeasureSpec(v.getLayoutParams().width, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(v.getLayoutParams().height, View.MeasureSpec.EXACTLY));
v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.RGB_565);
Canvas c = new Canvas(b);
v.draw(c);
return b;
}
Above code create real size bitmap properly. But all child position is not scaling with layout ratio.

Android magnify view and draw as bitmap

I've a camera and from this I take a photo and save a bitmap. Overlayed to this, there's a view pager, with different layouts. When I take a photo, I want to save the current overlayed layout too.
The problem is that photo and layout are both saved, the layout fix correctly in the bitmap area, but it's smaller in respect as was in the preview camera. I need to magnify the layout (to look as it was before to the photo) and to save it in my bitmap.
Before to take photo:
After (it's ok that the button is still there, it's just a test):
My code:
Bitmap cameraBitmap = BitmapFactory.decodeByteArray
(data, 0, data.length);
int wid = cameraBitmap.getWidth();
int hgt = cameraBitmap.getHeight();
bitmap = Bitmap.createBitmap
(wid, hgt, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(cameraBitmap, 0f, 0f, null);
ViewPager pager = (ViewPager) main_activity.findViewById(R.id.pager);
ScreenSlidePagerAdapter a = (ScreenSlidePagerAdapter) pager.getAdapter();
ScreenSlidePageFragment currFrag = (ScreenSlidePageFragment) a.instantiateItem(pager, a.getFocusedPage());
View currView = currFrag.getView();
Rect rect = new Rect();
rect.set(0, 0, wid, hgt);
//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);
currView.measure(widthSpec, heightSpec);
//Lay the view out at the rect width and height
currView.layout(0, 0, rect.width(), rect.height());
//Translate the Canvas into position and draw it
canvas.save();
canvas.translate(rect.left, rect.top);
currView.draw(canvas);
canvas.restore();
Why not create the bitmap with porportions. To do so, you need to create a Scalable Bitmap, and make the width like 1/4 of the screen and the height one fourth of the screen height.
To do so, first in the onCreate() method, also add in the following code to get the screenWidth and Height. -
metrics = getResources().getDisplayMetrics();
display = getWindowManager().getDefaultDisplay();
dpi = metrics.densityDpi;
size = new Point();
display.getSize(size);
screenWidth = size.x;
screenHeight = size.y;
To create a scalable bitmap use the following code, but remember to set the width and height values porportional such as the following --
public static int height = MainActivity.screenWidth* 4/10;
public static int width = MainActivity.screenWidth* 4/10;
bg = BitmapFactory.decodeResource(getResources(), R.drawable.bg);
if (width + heigt > 0) {
bg = Bitmap.createScaledBitmap(bg,
(width), (heigt),
false);
}
Also, to make sure the drawable images stay in quality, make sure to create seperate drawable folders, such as 'xxhdpi,and hdpi.'Refer to http://developer.android.com/guide/practices/screens_support.html. Hope that helps. Good luck.
I created a new bitmap with the view and I merged it with the previous bitmap, the result was good:
Bitmap cameraBitmap = BitmapFactory.decodeByteArray
(data, 0, data.length);
int wid = cameraBitmap.getWidth();
int hgt = cameraBitmap.getHeight();
bitmap = Bitmap.createBitmap
(wid, hgt, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(cameraBitmap, 0f, 0f, null);
ViewPager pager = (ViewPager) main_activity.findViewById(R.id.pager);
ScreenSlidePagerAdapter a = (ScreenSlidePagerAdapter) pager.getAdapter();
ScreenSlidePageFragment currFrag = (ScreenSlidePageFragment) a.instantiateItem(pager, a.getFocusedPage());
View currView = currFrag.getView();
Bitmap viewBitmap = CustomUtil.loadBitmapFromView(currView);
viewBitmap = Bitmap.createScaledBitmap(viewBitmap, wid, wid, false);
canvas.drawBitmap(viewBitmap, 0f, (hgt-wid)/2 /* to center my square view in my rectangular original bitmap */, null);
An utility method:
public static Bitmap loadBitmapFromView(View view) {
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;
}

Converting a view to Bitmap without displaying (with setHorizontallyScrollyng) in Android?

In my code, I have a LinearLayout with many TextViews (like a table). And I try to convert this to Bitmap. there is my code:
public static Bitmap convertToBitmap(LinearLayout view){
view.measure(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),
view.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bitmap);
view.draw(c);
return bitmap;
}
But, there is a problem.
Views witch has setSingleLine(true) not display text in bitmap.
Exactly, the problem is in setHorizontallyScrolling(true), which is calling in setSingleLine code...
I try use .setScrollTo(X), but what exactly X must be. X = 0 - not works.
Is there any, who knows how fix it?
EDIT: TextViews has fixed Width.
EDIT: Problem is only for TextViews with Gravity = Center. With gravity Left all's ok.
Try one of this code? It's a bit different from the one you posted above.
/**
* this actually also provided by Romain Guy, but let's try another for performance improvements
*/
public static Bitmap getBitmapFromView(View view, int width, int height) {
view.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
// Build the Drawing Cache
view.buildDrawingCache();
// Create Bitmap
Bitmap drawingCache = view.getDrawingCache();
if (drawingCache == null) {
return null;
}
Bitmap bitmap = Bitmap.createBitmap(drawingCache);
drawingCache.recycle();
view.setDrawingCacheEnabled(false);
return bitmap;
}
/**
* This method provided by Romain Guy, so it should do the job better, especially it includes case for listViews
*/
public static Bitmap getBitmapFromView(View view, int width, int height) {
//Pre-measure the view so that height and width don't remain null.
view.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
//Assign a size and position to the view and all of its descendants
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
// Create bitmap
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.RGB_565);
//Create a canvas with the specified bitmap to draw into
Canvas canvas = new Canvas(bitmap);
// if it's scrollView we get gull size
canvas.translate(-view.getScrollX(), -view.getScrollY());
//Render this view (and all of its children) to the given Canvas
view.draw(canvas);
return bitmap;
}

Converting a view to Bitmap without displaying it in Android?

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.
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();
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 :)
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
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;
}
There is a great Kotlin extension function in Android KTX: View.drawToBitmap(Bitmap.Config)
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*/);
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.
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.
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
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,
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()
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
}
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;
}
view.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);

Categories

Resources