I have a surfaceview in my activity. Initially some circles are drawn using
Canvas c = SurfaceView.getHolder().lockCanvas();
and
SurfaceView.getHolder().unlockCanvasAndPost(c);
Later on I want to draw on a very small part of the surfaceview. I do not want to redraw the whole surfaceview. Is it possible to update just the required part?
Use lockCanvas(Rect dirty) instead. Pass in the area you want to redraw.
Note that dirty is an in-out parameter. The Rect may be expanded to cover a larger area, and you need to set every pixel it covers. So if (for example) SurfaceView no longer has access to the previous pixels, it'll expand Rect to cover the entire surface.
Because SurfaceView is double- or triple-buffered, the implementation will copy chunks of pixels from the previous buffer. Since the Canvas you get from a SurfaceView is never hardware-accelerated, specifying a dirty rect is often a performance win.
Related
I have a relativelayout with three full screen child views of the same custom view class. I am wondering whether I should be worried about memory. Judging by this answer:
Understanding Canvas and Surface concepts
All views get passed the same canvas with underlying bitmap to draw on so that the memory is not tripled. Can anyone confirm?
It would make sense, otherwise a full screen textview would be very inefficient.
Bonus: is the purpose of canvas to define a drawable area of a bitmap and translating the view coordinates to bitmap coordinates?
As per the documentation http://developer.android.com/guide/topics/graphics/2d-graphics.html#draw-with-canvas:
When you're writing an application in which you would like to perform specialized drawing and/or control the animation of graphics, you should do so by drawing through a Canvas. A Canvas works for you as a pretense, or interface, to the actual surface upon which your graphics will be drawn — it holds all of your "draw" calls. Via the Canvas, your drawing is actually performed upon an underlying Bitmap, which is placed into the window.
In the onDraw(Canvas canvas), you are given a canvas object. This canvas has an underlying bitmap. All views are not given the same canvas. Canvas is just a layer above the common bitmap (which is pixels on the screen). canvas offers you to manipulate the bitmap as much as you want. So every view has a canvas, but not it's own bitmap.
So no, as far as memory is concerned, three view doesn't mean memory is tripled, because there is just one bitmap. You could however create your own bitmap, if you do so, then you will be jogging up the memory. If you create 3 bitmaps with size of the screen, your memory will be tripled.
Working with a SurfaceView to make a 2d game i do not know who to put a background image in it efficiently.
I want to avoid drawing it each frame because it is an static image, any help?
The SurfaceView's Surface is a single layer. You can specify a dirty rect to reduce your draw area when you lock the Canvas, but you have to draw everything in the rect every frame -- background and all.
You could use a pair of SurfaceViews, one at the default Z-ordering, one at "media" depth. Use SurfaceView.setZOrderMediaOverlay() to do that. The layers will be composited by the system before being rendered.
If you were using OpenGL ES rather than Canvas, it'd generally be more efficient to just redraw the background from a texture each time. (If HWC runs out of overlays, that's essentially what SurfaceFlinger is doing anyway.)
See the "multi-surface test" in Grafika for an example of using multiple overlapping surfaces.
Is there way to prevent drawing of parts of objects, like bitmaps or paths, which go beyond the borders of Canvas in SurfaceView?
When I gradually scale an object beyond the Canvas' size, especially with blurred paint, it all slows down to a stand still and I have to wait few seconds to get the control back -- it is not responsive. I scale an object by moving my finger over the screen; if I do it too fast and scale it up, then it really slows down drawing.
I did not have the same problem when using just ordinary View canvas, so don't know what is slowing it down. It's as though SurfaceView responds too fast and then gets congested.
So one idea to improve this, is to prevent drawing outside canvas, but not sure if SurfaceView has such clipping options.
One solution perhaps is by using one of the Canvas .drawBitmap methods that accepts a source Rect / RectF argument, so that only the visible portion of the Bitmap is rendered. I've just done this on my own project to speed up SurfaceView performance where I have several Bitmaps that are sometimes only partially in view.
I'm not sure if canvas would actually draw things beyond the dimensions, that wouldn't be a smart thing to do. Still, you can use clipRect() to define a clip region.
Are you applying filters like blur per frame ? that might be pretty expensive.
Also, are you triggering draw calls on touch event ? Touch events are generated very quickly and will flood the rendering thread. You might want to pick up events at a slower rate: https://groups.google.com/forum/?fromgroups=#!topic/android-developers/Oe6k1_bm38o
When you put the SurfaceView on the layout of your activity, you can use the padding attributes like:
android:paddingLeft="20dp"
android:paddingTop="20dp"
android:paddingRight="20dp"
android:paddingBottom="20dp"
It works like this:
To draw your canvas you can draw dinamically the borders with:
canvas.drawLine(1,1,canvas.getWidth()-1,1); //Top border
canvas.drawLine(1,1,1,canvas.getHeight()-1); //Left border
canvas.drawLine(1,canvas.getHeight()-1,canvas.getWidth()-1,canvas.getHeight()-1); //Bottom border
canvas.drawLine(canvas.getWidth()-1,1,canvas.getWidth()-1,canvas.getHeight()-1); //Right border
I used surfaceview to create a graph. This graph changes continuously over time.
When I use postInvalidate method to update graph, surfaceview is redrawn but I don't want to redraw xy-axis. what should I do?
I believe what you are looking for is the lockCanvas(Rect dirty) method from the SurfaceHolder class.
http://developer.android.com/reference/android/view/SurfaceHolder.html
Official documentation:
"Just like lockCanvas() but allows specification of a dirty rectangle. Every pixel within that rectangle must be written; however pixels outside the dirty rectangle will be preserved by the next call to lockCanvas()."
Therefore you can just change the graphs contents, leaving any pre-drawn axis
I'm writing an android application that builds an offscreen bitmap that is (say) 640*480 pixels and displays that in a SurfaceView. Note that the size of the image is larger than the actual display on the handset.
The way that the application works is that it initially displays the upper left corner (0,0) of the image and then I would like to be able to scroll around the image.
My Surfaceview class implements the onMeasure callback and returns a size of 1024*768 which displays the area of the image that corresponds to the phone screen. I'd like to implement support for scrolling which I'm finding near impossible due to the very limited documentation (just bunches of classes and calls with no coherency). I've tried just calling scrollBy and that doesn't work.
If anyone has any pointers about how to proceed this would be much appreciated.
Once you start using a SurfaceView, you are moving yourself into a world where you have a blank area of pixels, and you are on your own to draw them how you want. You do everything yourself - and that means you need to implement scrolling yourself as well. It's not trivial to do, but neither is it too difficult.
One way to do it is the following:
- Extend SurfaceView and implement SurfaceHolder.Callback
- Use getHolder().addCallback( this ) to grab the callback
- Use OnTouch events to implement the scrolling behavior (i.e., when responding, pick up the x,y position from the MotionEvent and set the offset appropriately).
Implement a thread to handle the updating of the image. There are examples of this around on the web (sorry - don't have any to hand right now).
Alternatively, use an off-screen bitmap and have it drawn by a normal ImageView (size the ImageView to the size of the bitmap and place it in a scroll view).
Edit:
Seeing that this is still getting views, it might be worth pointing out that since this time, I've implemented and released as open-source a small library that implements a framework for the above functionality (inspired by many comments here on SO, among others). It can be found on Github: https://github.com/micabyte/android_game
I just solved this for my own game, my surfaceView is an adaptation of the LunarLander example by Google.
Everything is the same except I'm drawing on my own private Bitmap with my own private Canvas in the doDraw method, and then copying some of it to the canvas I'm given using the code below.
Rect src = new Rect(mScrollX, mScrollY, mScrollX+mSurfaceWidth, mScrollY+mSurfaceHeight);
Rect dst = new Rect(0, 0, mSurfaceWidth, mSurfaceHeight);
surfaceCanvas.drawBitmap (mLevelBitmap, src, dst, null);
The Rect objects define how drawBitmap scales/transforms pixels from the source Bitmap (my private bitmap) to the destination (the Canvas I was given to draw on in doDraw()
mScrollX and Y are the upper left corner of the rectangle that will be displayed (relative to the big private bitmap)
mSurfaceWidth/Height is the size of the rectangle that will be displayed
Just make sure the scroll values are within acceptable ranges [0,privateBitmapWidth-mSurfaceWidth]
update the scroll values yourself however you please, (in onTouchEvent() for example)