Is it possible to get the drawing cache with the views behind the view? For example, I have a semi-transparent view and I can see views behind it. So, can I get the drawing cache of this view with the behind views visible?
Code where I'm adding the view to WM:
final View screenshotView = new View(this) {
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (!changed) {
buildDrawingCache();
final Bitmap bitmap = Bitmap.createBitmap(getDrawingCache());
final File file = new File("/sdcard/test.png");
try {
file.createNewFile();
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, ostream);
ostream.close();
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
}
};
WindowManager.LayoutParams screenShotViewLp = new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);
screenShotViewLp.width = WindowManager.LayoutParams.MATCH_PARENT;
screenShotViewLp.height = WindowManager.LayoutParams.MATCH_PARENT;
screenShotViewLp.gravity = Gravity.LEFT | Gravity.TOP;
wm.addView(screenshotView, screenShotViewLp);
[EDIT] according to last edits of the question I think this answer doesn't fit anymore. Adding a view directly to the WindowManager infact leads the discussion to a well-known problem: taking a screenshot programmatically in Android is not allowed, and then the idea exposed below seems totally impractical.
So please don't undervote.[/EDIT]
AFAIK this can't be achieved via the drawingCache of the single view. In order to get also the other views on the background you should take the drawingCache of the topmost node in the view tree, that is the content of the containing Activity. Getting such a bitmap is quite simple:
View root = currActivity.getWindow().getDecorView().findViewById(android.R.id.content);
root.setDrawingCacheEnabled(true);
Bitmap bmp = root.getDrawingCache();
Then, in order to get only the portion of bitmap you are interested in, you are forced to crop the generated bitmap according to View.getLocatonInWindow(int[]) values.
Haven't proved myself, but I'm quite confident it should work.
Related
I have created a drag and drop activity in my app for reordering items using the code found here.
one of the activities involves reordering images, so I added an image view to the layout and adjusted the code to add the right images and all that stuff. Everything works fine except that when grabbing items, the first item you grab from each slot is always what shows up whenever you grab any other item from that slot.
So for example, say i have items a, b, c, and d, if I grab item a and move it to the end, b is now in that first slot. if I grab item b now, the floating view that gets dragged around is still showing a. When it's dropped, everything looks fine and is in the new order that it is supposed to be in. Now in that same example, if I take the second item, which is now item c, and move it anywhere, the same thing happens. Dragging the first item always shows a and the second always shows c.
I can also take item a, move it to the second, then third, then fourth etc, slot and then no matter which item I grab, item a is always the one showing when an item is being dragged.
I'm confused as to what is causing this, i think it may be something to do with this section of code, specifically with the drawing Cache.
private void startDrag(int itemIndex, int y) {
stopDrag(itemIndex);
Log.i("startdrag","view " + itemIndex);
View item = getChildAt(itemIndex);
if (item == null) return;
item.setDrawingCacheEnabled(true);
if (mDragListener != null)
mDragListener.onStartDrag(item);
// Create a copy of the drawing cache so that it does not get recycled
// by the framework when the list tries to clean up memory
Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
WindowManager.LayoutParams mWindowParams = new WindowManager.LayoutParams();
mWindowParams.gravity = Gravity.TOP;
mWindowParams.x = 0;
mWindowParams.y = y - mDragPointOffset;
mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
mWindowParams.format = PixelFormat.TRANSLUCENT;
mWindowParams.windowAnimations = 0;
Context context = getContext();
ImageView v = new ImageView(context);
v.setImageBitmap(bitmap);
WindowManager mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
mWindowManager.addView(v, mWindowParams);
mDragView = v;
}
The original app also occasionally shows the wrong item when dragging, but I cannot get it to repeat reliably, which is why I think it has something to do with the drawing cache. My version has images and its probably trying to save memory by reusing the old views. I really don't know, if there's more I need to show just ask.
Thanks in advance.
Rather than using the drawing cache, try instead to get the bitmap from the view directly. This is how Google does it in their DynamicListView as you can see below. I've been using a lightly modified version of it without issue. You can find more info here : https://www.youtube.com/watch?v=_BZIvjMgH-Q
/** Draws a white border over the screenshot of the view passed in. */
private Bitmap getBitmapWithBorder(View v) {
Bitmap bitmap = getBitmapFromView(v);
Canvas can = new Canvas(bitmap);
Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(LINE_THICKNESS);
paint.setColor(getResources().getColor(R.color.transparent_white));
can.drawBitmap(bitmap, 0, 0, null);
can.drawRect(rect, paint);
return bitmap;
}
/** Returns a bitmap showing a screenshot of the view passed in. */
private Bitmap getBitmapFromView(View v) {
Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas (bitmap);
v.draw(canvas);
return bitmap;
}
I have an Android related issue:
I am trying to centre a logo on the screen of my device, but it won't position correctly.
I am using the following function:
public void ImageCentered(int ID){
ImageView iv = (ImageView) findViewById(ID);
int x = 0;
int y = 0;
RelativeLayout.LayoutParams position = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
x = (screenWidth/2)-(iv.getWidth()/2);
y = (screenHeight/2)-(iv.getHeight()/2);
position.setMargins(x, y, 0, 0);
iv.setLayoutParams(position);
}
This could should work,but it won't. The image is set off slightly to the right and bottom like in this image:
https://www.dropbox.com/s/tf7r1u1xqcmb9t9/2014-08-16%2018.36.04.png
Now, the strange thing is, when I use the following code:
public void ImageCentered(int ID){
ImageView iv = (ImageView) findViewById(ID);
int x = 0;
int y = 0;
RelativeLayout.LayoutParams position = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
x = (screenWidth/2)-(iv.getWidth()/2);
y = (screenHeight/2)-(iv.getHeight()/2);
position.setMargins(x, y, 0, 0);
Message(IntToStr(x)+", "+IntToStr(y));
iv.setLayoutParams(position);
}
this is the result:
https://www.dropbox.com/s/mjw80zlzkav6dzs/2014-08-16%2018.41.16.png
Side note: The text in the Message() function does not matter, nor does its position within the ImageCentered() function.
I am not calling the function in my OnCreate(), as the width and height of the image would always return 0, so I looked something up and found this:
#Override
public void onWindowFocusChanged(boolean hasFocus){
ImageCentered(R.id.image);
}
This piece of code is in my MainActivity.java file, whereas the ImageCentered() function is in my UtilLib.java file.
So, I was wondering: What's going on here? Why does the code work when I pop in a Message() but not when I leave it out?
Sure, I can try hardcoding the data, but what about smaller/bigger screens?
I hope an Android guru can help me out here, as I've been struggling with this for quite some time now.
EDIT
Just noticed something interesting when pressing "OK" on my Message:
https://www.dropbox.com/s/a7lu6588hy1opw7/2014-08-16%2018.51.55.png
My guess is that my problem lies there, but after clicking the "OK" button once more, the data is "492, 207" again. scratches head
Assuming rom your code that the ImageView is inside a RelativeLayout, You could also do:
// get imageview layout params or create new ones
RelativeLayout.LayoutParams params = imageView.getLayoutParams();
params.addRule(RelativeLayout.CENTER_HORIZONTAL);
this way the image will be automatically centered without all those manual calculations!.
You can also specify it in the XML with
<ImageView .....
android:layout_centerHorizontal="true"/>
I've ultimately decided to just hardcode the x and y coordinates, and later on use some sort of scaling-conversion to position them properly, unless someone can provide me with a better method of fixing this.
UPDATE
So, after googling after a while (again), I have finally found the answer and created two functions:
public int GetImageHeight(int ID){
ImageView iv = (ImageView)findViewById(ID);
iv.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
iv.layout(0, 0, iv.getMeasuredWidth(), iv.getMeasuredHeight());
return iv.getMeasuredHeight();
}
public int GetImageWidth(int ID){
ImageView iv = (ImageView)findViewById(ID);
iv.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
iv.layout(0, 0, iv.getMeasuredWidth(), iv.getMeasuredHeight());
return iv.getMeasuredWidth();
}
All you have to do is pass the ID of the image you made in your xml file, like so:
GetImageHeight(R.id.logo)
I have the following code:
llColors = (LinearLayout) findViewById(R.id.llColorSpect);
llColors.setOnTouchListener(llTouch);
width = llColors.getWidth();
height = llColors.getHeight();
Log.i("LAYOUT WIDTH", "width" +width); //shows 0
Log.i("LAYOUT HEIGHT", "height" +h); //shows the correct height
What makes me confused is why is the LAYOUT WIDTH is 0. In the XML, I set the layout width to match_parent and height to wrap_content.
How do I take the Bitmap that I am decoding below and fill in the layout above width fill_parent? Do I use LayoutParams?
bitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.palette2);
The XML code is this:
<LinearLayout
android:id="#+id/llColorSpect"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#drawable/palette2"
android:layout_alignParentBottom="true" >
</LinearLayout>
What I am trying to accomplish is lehman term is, take the layout and fill it with a background image and use the pixel to get X and Y coordinate.
You are calling getWidth() too early. The UI has not been sized and laid out on the screen yet.
Try getting size after overriding the onWindowFocusChanged() function:
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
//Here you can get the size!
}
Have a look at this discussion
If you want your bitmap to match parent height and width you can use this:
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
imageView.setImageBitmap(bitmap);
imageView.setLayoutParams(params);
Hope this helps :)
As far as the width being 0, the layout may not be finished being drawn yet. I used OnGlobalLayoutListener to know when it had been drawn. Here is a bit of code how I have done it to make my layout a Bitmap so I could print it. A little different but same concept.
#Override
public void onGlobalLayout()
{
String path = null;
try {
path = Environment.getExternalStorageDirectory().getPath().toString();
FileOutputStream out = new FileOutputStream(path + "/testPrint" + "0" + ".png");
Bitmap bm = Bitmap.createBitmap(root.getDrawingCache(), 0, 0,
root.getWidth(), root.getHeight());
bm.compress(Bitmap.CompressFormat.PNG, 90, out);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
where root is the inflated layout that I needed to get the width of.
and you can set the listener with something like
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener((OnGlobalLayoutListener) root.getContext());
OnGlobalLayoutListener Docs
I have done a sample painting app using APIDemo's FingerPaint app. Instead of the "usual" pattern of setContentView(R.layout.main) it uses a class MyView that extends View and sets content as setContentView(new MyView(this)); now whatever I draw I want to save it in the SDCard. For this I require to know the rootview using getRootView. This is got by the object of layout(for ex: LinearLayout L1 = new...) L1.getRootView. Because I am using this MyView, I am not able to get the rootview nor able to save the bitmap.
myview.setDrawingCacheEnabled(true);
myview.requestFocus();
myview.getRootView();
System.out.println("MYVIEW = "+myview);
myview.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
myview.layout(0, 0, myview.getMeasuredWidth(), myview.getMeasuredHeight());
myview.buildDrawingCache(true);
mBitmap = myview.getDrawingCache();
//System.out.println("myview.getDrawingCache() = "+newview.getDrawingCache());
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
myview.setDrawingCacheEnabled(false); // clear drawing cache
System.out.println("BITMAP = "+mBitmap);
File f = new File(Environment.getExternalStorageDirectory()
+ File.separator + "test.jpg");
try {
f.createNewFile();
FileOutputStream fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());
} catch (Exception e)
{
e.printStackTrace();
}
I want to know how do I save my drawing using a menu button click?
Thank you
After a lot of effort into research I bumped into this http://blahti.wordpress.com/2010/11/18/how-to-save-jpeg-files-in-the-android-emulator/ This solved my issue of saving the drawing.
I have an app that displays some data graphically. It creates two Views to draw graphics in and adds them to my layout. Each view shows the data differently way but each View implements onSizeChanged() the same:
protected void onSizeChanged(int curw, int curh, int oldw, int oldh) {
if (bitmap2 != null) {
bitmap2.recycle();
}
canvas2= new Canvas();
bitmap2 = Bitmap.createBitmap(curw, curh, Bitmap.Config.ARGB_8888);
canvas2.setBitmap(bitmap2);
}
The views are invoked thusly:
LinearLayout myLayout = (LinearLayout)findViewById(R.id.revlay);
GraphView1 graphView1 = new GraphView1(this, theEventArrayList);
myLayout.addView(graphView1);
GraphView2 graphView2 = new GraphView2(this, theEventArrayList);
myLayout.addView(graphView2);
Always the first onSizeChanged() that gets called gets a height of 652 and a width of 480; the second one gets a height of 0, which causes createBitmap() to fail. If I reversed the order of the above invocation then graphView1 would fail that way. I'd like each bitmap to have about half the area.
Thanks in advance for explaining what's going on!
its difficult to answer your question without knowing further implementation details about your graphviews and the layout parameters of the LinearLayout.
But assuming your LinearLayout has a horizontal orientation try this:
GraphView1 graphView1 = new GraphView1(this, theEventArrayList);
GraphView2 graphView2 = new GraphView2(this, theEventArrayList);
myLayout.addView(graphView2, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 0, 1));
myLayout.addView(graphView1, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 0, 1));
this will tell the LinearLayout to arrange your views so that they equally divide the horizontal space between themselves.