I'm working on a simple painting app and trying to implement the functionality of giving more space to draw when a user requests, which I thought could be done by simply enabling scrolling of my CustomView class(which is contained in a LinearLayout then in a ScrollView class). If I didn't resize the CustomView class by running Chunk 1, Chunk 2 works fine(It simply saves one screen of drawing. At this time, there is no scrolling.) A bummer! Chunk 2 fails(view.getDrawingCache() returns null) when Chunk 1 is run (At this time, scrolling is enabled). I want to save the entire view including the part left out due to scrolling.
Chunk 1:
CustomView view = (CustomView) findViewById(R.id.customViewId);
height = 2 * height;
view.setLayoutParams(new LinearLayout.LayoutParams(width, height));
Chunk 2:
CustomView view = (CustomView) findViewById(R.id.customViewId);
view.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);
These two chunks of codes are independent and small parts in two different methods.
Edited: SOLVED
After billions of Google queries, the problem is now SOLVED ;)
I referred to this thread, https://groups.google.com/forum/?fromgroups=#!topic/android-developers/VQM5WmxPilM.
What I didn't understand was getDrawingCache() method has the limit on the View class's size(width and height). If the View class is too big, getDrawingCache() simply returns null.
So, the solution was not to use the method and instead do it like below.
CustomView view = (CustomView) findViewById(R.id.customViewId);
Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(),
view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas bitmapHolder = new Canvas(bitmap);
view.draw(bitmapHolder);
// bitmap now contains the data we need! Do whatever with it!
You need to call buildDrawingCache(), before being able to use the bitmap.
The setDrawingCache(true) thing just sets the flag and waits for the next drawing pass in order to create the cache bitmap.
Also don't forget to call destroyDrawingCache() when you no longer need it.
Related
I'm trying save a view to a bitmap without waiting for it to be rendered.
I request the rendering of a RecyclerView and then do:
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Config.ARGB_8888);
View view = activity.findViewById(R.id.my_view);
view.draw(new Canvas(bitmap));
But if I save the bitmap right away it will be empty because the views in under my recycler view are still being drawn I guess.
I tired using the ViewTreeObserver and adding an OnGlobalLayoutListener but the problem persisted - that callback is called too soon.
How can I reliably wait for the bitmap / canvas to be filled? Is there a specific callback for that?
PS: no hard coded delays allowed :)
A good approach is to override the RecyclerView onMeasure method, and get the width/height values, if they are 0 they are just created, if they are greater, do you onDraw after the measure ends.
in my layout I have a background, button and a textView, the button here serves to convert my view to bitmap and save it
I managed to do that but my png Image contains the save button as well,
I'm using this line to get the drawing from my layout :
Bitmap cache = vw.getDrawingCache();
What I want to achieve is that before getting the screen from the view , I want it to not consider my button.
Thanks in advance.
Using canvas to draw layout like this
Bitmap bitmap = Bitmap.createBitmap(layout.getWidth(), layout.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
layout.draw(canvas);
Have you looked at setting the component to visibility gone?
Button saveButton = (Button) findViewById(R.id.save_button);
saveButton.setVisibility(View.GONE);
Then after you get your snapshot make sure you do
saveButton.setVisibility(View.VISIBLE);
The problem with this solution is that the screenshot will still contain the gap where the button was. The view does not get redrawn before the bitmap is saved. I have tried calling invalidate() and requestLayout() with no success (which is why I found your question).
Also, another alternative is to do something like what Anand said by drawing it to a Canvas. This is useful if your view is larger than the screen. By drawing it to a Canvas you can get the entire view. By going this route, you can have a container with your background and TextView and have the button outside that container. You can then get just the view with the container. (I can't use this solution since my button is in the middle of my desired data)
View v = findViewById(R.id.my_view); // the view containing your text view
// not needed since it is outside your my_view now
// v.findViewById(R.id.save_button).setVisibility(View.GONE);
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.layout(0, 0, v.getLayoutParams().width, v.getLayoutParams().height);
v.draw(c);
// not needed since it is outside your my_view now
// v.findViewById(R.id.save_button).setVisibility(View.VISIBLE);
// your Bitmap "b" now contains the image you want
I've read pretty much every post on this topic, but none of them seem to help.
I'm trying to capture a screen shot of the current screen. For this I'm using getDrawingCache. Here's my code:
mRootView.get().setDrawingCacheEnabled(true);
mRootView.get().buildDrawingCache();
Bitmap screenShot = Bitmap.createBitmap(mRootView.get().getDrawingCache());
mRootView.get().setDrawingCacheEnabled(false);
The resulting bitmap is always black.
mRootView is a weak-reference to the drawer layout, which is my root view.
Here's what I've tried:
Adding a measure and layout call (although this shouldn't be needed since this code runs when a button is pressed, so the view should already be layed out).
Setting the layer type to LAYER_TYPE_NONE before calling setDrawingCacheEnabled(true)
Using a different view as the root view (for example, a ViewPager inside the DrawerLayout).
Nothing seems to work and I've run out of ideas.
You can draw your view to canvas-
pass your main layout reference to this method-
Bitmap file = save(layout);
Bitmap save(View v)
{
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.draw(c);
return b;
}
If above solution not working so just follow this link might be helpful convert view to bitmap
I have a ListView, and its child Views contain many calls to the Canvas on their onDraw() methods (such as drawPath() etc). I have found that when you scroll the ListView, the calls to onDraw (which are frequent) cause really jerky scrolling. I have found that you can eliminate this problem by replacing each View in the ListView by an ImageView where the image Bitmap is created from the original View using code like this.
view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
int bitmapWidth = view.getMeasuredWidth();
int bitmapHeight = view.getMeasuredHeight();
view.layout(0, 0, bitmapWidth, bitmapHeight);
Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
This solves the problem of jerky scrolling. However the problem is that the method Bitmap.createBitmap() seems prone to throw an OutOfMemoryError. I have read advice on this site that you should never use this method. Does anyone know how I can best get around these problems?
A few things:
1) Post more source
2) read http://developer.android.com/training/improving-layouts/smooth-scrolling.html
3) consider using an ArrayAdapter http://developer.android.com/reference/android/widget/ArrayAdapter.html
You may want to track the number of times you call create bitmap, and how garbage collection is being handled there. If you're not properly recycling your views, and you're creating lots of these bitmaps you going to very rapidly overwhelm your available memory.
For efficiency in this regard you can look at bitmap caching, ala:
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
Months later I have found a solution. Before I started using Bitmaps I was unwittingly allocating a large number of objects in the onDraw() methods. I was not getting any warnings from Android Studio because the new keyword was being used in a method called from onDraw rather than onDraw itself. I discovered that if you accidentally write new Paint() in a loop rather than reusing a Paint object, it seriously harms the performance of scrolling.
I now only have a handful of Paint objects and a single Path object in my application (and no Bitmaps at all) and all these problems have disappeared. Hopefully somebody reading this will learn from my mistake!
I am trying to create a bitmap from a View that is not displayed yet to set it as a texture with OpenGL in android.
The problem is that when I create my bitmap the layout parameters of my View have 0 value cause the onLayout() function has not been called yet.
Is there a way to "simulate" a layout or do a "background layout" to get the right layout params ?
I was thinking about a Frame layout having two views, the background one would be used to create my bitmap as the layout would have been done.
Thanks.
You indeed need to layout your View before drawing it into a Bitmap. Simply call
myView.measure(...);
then
myView.layout(0, 0, myView.getMeasuredWidth(), myView.getMeasuredHeight());
I have posted an example on how to do this in one of the following presentations: http://www.curious-creature.org/2010/12/02/android-graphics-animations-and-tips-tricks/
So, some edits after the question was clarified :)
I suggest you create the bitmap independently of the view, then scale it to the same size as the view once the view is created. Create an arbitrary size bitmap that allows you to render what you want to it ..
// This in some class that renders your bitmap independently, and can be
// queried to get the bitmap ...
Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_4444);
// render something to your bitmap here
Then after your view is created, take the precreated bitmap and rescale it to the right size (I've not actually checked this by compiling it -- might be errors):
Rect original = new Rect(0, 0, 100, 100);
RectF destination = new RectF(0.0, 0.0, (float)myView.getWidth(), (float)myView.getHeight());
Canvas canvas = new Canvas();
canvas.drawBitmap(myRenderingClass.b, original, destination, null);
myView.draw(canvas);