I need update a small portion of a custom view in order to display a small animation. The rest portion of the view has only static image. The most straightforward would be to obtain the canvas of the view and update only that particular portion directly. But I can't really find anyway to get the view's canvas object outside of the view::onDraw method.
The only alternative I know is this: call view::invalidate() with a specified rectangle to minimize the drawing flicker. I have the code to update the entire view within onDraw. So the best thing to do is to detect the clipping rect and only run the code to update the specified area, in order to minimize CPU usage as well?
I guess I will try to answer this question myself to the best my knowledge so far.
There is no direct access to the canvas outside of the onDraw method.
Although we can detect the clipping rect with the function Canvas.getClipBounds(), the getClipBounds function always return the entire view area if GPU is enabled. When GPU is not used, getClipBounds() returns the actual dirty area. Since there is a GPU in most phones, it makes the function getClipBounds pretty much useless.
Related
In the project I am working on I decided to use SurfaceView instead of a custom double buffered medium. It provides everything I need and it's already double buffered.
The problem is that it wont let me specify multiple dirty rectangles to redraw.
SurfaceView.lockCanvas(Rect) only allows single rectangle and without parameter it's pretty expensive to redraw whole thing. Another solution to call lockCanvas(Rect) for each Rect causes eye-bleeding blinking in the screen, obviously.
Do you have any solution giving the opportunity staying inside Android API field, if not do you have any external alternatives I can use?
If you know the dirty areas before you need to call lockCanvas (sounds like you might), you could calculate a "super rectangle" that locks an area that contains all of your rectangle. For example if your rectangles are (using l,r,t,b coordinates) [0,10,0,20] and [15,30,10,35], your super rectangle would be [0,30,0,35].
I've created an activity that calls a DrawView extends View, I draw on a Canvas something like a binary tree. The canvas size is greater than the screen size, and the onDraw function draws all the canvas, not only the visible part of it. I've added an horizontal and a vertical scrollView, and obviously onDraw is called again and again to refresh the view.
I was wondering if I can draw the canvas on an image (a bitmap or something like that) using that image to show the tree, without the need to recall the onDraw function.
If I can't do this, what can I do in order to get a faster view?
If you're using API 11 or above you can try using the hardware acceleration attribute in the application tag in your manifest.
<application ... android:hardwareAccelerated="true" ...>
Other than that you could consider using another rendering than View, have a look at SurfaceView too.
Generally, I think you shouldn't override View unless you're building a UI component (like a Button), don't quote me on this though.
Technically the canvas draws on a Bitmap. So it's actually the Bitmap that's bigger than the screen. Why are you making the Bitmap bigger than the screen? Bigger Bitmaps = more memory usage - which could slow things down a bit. You only really draw within the clipped bounds of the screen - you may think you're drawing off screen but you're not. Drawing on another Bitmap won't help you - you're already doing that. Without looking at your code you could try:
only draw what changes in the binary tree by calling invalidate(rect) which only repaints what has changed
if you binary tree is meant to be static, just create a png/jpg of it and display that image versus drawing it all yourself
make sure you're not calling invalidate more than you need to
I have made my own canvas class which extends an imageView. My onDraw() method draws out the users gps position and I keep calling this onDraw method every time the user moves. My problem is I also want to draw out a gps trail which only needs to be drawn once (doesnt need to be updated when a user moves). I am wondering is it possible to have more than 1 onDraw method or is there any way of separating 1) the user location and 2) the gps trail??
My reason is I do not want to waste memory by redrawing the gps route everytime the users gps position changes. It is a waste.
Have you seen performance take a hit? If not, don't worry about it. I would think that this would be wasting CPU cycles if anything... not memory. So if the app seems fast enough already, don't worry about optimizing it.
If your app is a bit laggy, and you've found that the trail is the bottleneck... I would suggest caching it into a bitmap. This way, you will still have to draw the trail, but you will not have to calculate the coordinates of the trail on each frame.
I have had to solve a somewhat similar problem recently and I'll explain briefly what I did in case it's of any help.
What you can do is use multiple overlapping Views, where one may contain the background graphics that you don't want to redraw often, and a foreground View that contains the graphics that are frequently updated. Then, to gain performance, you can design the background View's onDraw() so that it is backed by a Bitmap that you then retain as a class variable. In the very first onDraw() of your background graphics, you do the relatively slow drawing to Canvas. In subsequent calls to onDraw(), you simply draw that Bitmap to Canvas.
I've just done this myself. Basically what my application does is display a number of graphical gauges. Those gauges have lots of graphics that are drawn just once (gauge face, number legends), and the pointer graphic that needs to be redrawn over and over as the model data changes. First of all, I split the background graphics and moving foreground graphics into separate overlapping Views. Now, invalidating and redrawing the foreground pointer graphic of course causes anything it overlaps to be invalidated too, so the onDraw() method for the background graphics View is being called each time the pointer View is redrawn. The background View only needs to draw the background graphics once, but retains the Bitmap that backs the canvas, and in subsequent onDraw() calls it draws this bitmap back to Canvas (which is a lot faster than initially creating the graphics using Path() objects).
Bitmap bm;
....
protected void onDraw(Canvas canvas){
if(null==bm){
bm=Bitmap.createBitmap(getMeasuredWidth(),getMeasuredHeight(),Bitmap.Config.ARGB_8888);
// do your slow vector graphics drawing to Canvas here
}
Paint drawPaint = new Paint();
drawPaint.setAntiAlias(false);
drawPaint.setFilterBitmap(false);
drawPaint.setDither(false);
canvas.drawBitmap(bm, 0, 0, drawPaint);
}
Well, there can't be more than 1 onDraw method, assuming that I understood your question correctly. You will need to think about alternate approaches about how to handle this.
#DeeV suggested, that can be a solution for you.
I have a custom view that represents simple gameboard with several tiles. I have a 2-d array that stores the currently displayed image (0 for no image) for a tile.
Currently within my onDraw() method I loop through the array and display the images for the tiles. However the only time I invoke invalidate() without any args is when I reset the board. In all other places I only invoke an invalidate(Rect) for the required tile that needs to be updated.
Is this above implementation of onDraw() in-efficiently drawing the view? If so what would be a better way to do this? (also does invoking invalidate(Rect) guarantee updating only the 'Rect' region OR is this just a recommendation to Android that may be ignored?).
Appreciate any clarifications with this.
Is this above implementation of onDraw() in-efficiently drawing the view?
It is good enough. Otherwise you'd like to learn OpenGL to make use of hardware acceleration in your drawings, but all of this depends on if your tiles are changing every frame or how many tiles you have.
also does invoking invalidate(Rect) guarantee updating only the 'Rect' region OR is this just a recommendation to Android that may be ignored?
It updates the Rect area, infact Romain Guy uses invalidate(Rect) for the default home launcher application in order to not update the whole screen everytime the home screen needs an update.
I'd like to include a simple draw feature on my app. A friend told me I can't use canvas because I need to control the frame buffer and not redraw the entire screen every for every frame. He suggested I find a openGL 2d engine. I've searched around and I've only found Rokon which doesn't have any documentation and the examples aren't quite what I need.
Any suggestions?
I don't really understand what the problem is?
If you simply wish to redraw some portion of the canvas you can use invalidate(rect).
http://developer.android.com/reference/android/view/View.html#invalidate()
Just create a custom veiw by extending the view class. in this custome view override the onDraw method. Android itself takes care of the pixels that have been changed and calling invalidate only refreshes the pixels that have been marked dirty