I am currently developing a single view android app using the simple invalidate/onDraw strategy, and have noticed that the canvas always appears erased prior to the invocation of the onDraw method. Does anyone know of a simple way to preserve the current canvas contents prior to the onDraw method call? Any help would be most appreciated.
Use a bitmap to store the last drawn state and in every onDraw method call, draw the bitmap to the canvas before anything else.
Related
Hi I am new to Android Canvas. I want to know how can I draw on canvas using a bitmap.
Basically I want to be able to draw multiple figures in my canvas dynamically. After reading about canvas I figured out this much that for each figure I may have to create a new bitmap attach a canvas to it draw a figure in that bitmap and finally draw that bitmap in the canvas of onDraw() method using drawBitmap function in order to view it on screen. Hope I am right till this part ? If not please correct me. I am open for your opinions and suggestions :)
Also I would like to know if I can apply onTouch event separately to the bitmaps or canvas created dynamically or it can be only applied to canvas of onDraw cause I want the images which are being drawn in my canvas to be able to move at users will?
You are right about the first part. However in second part u cannot add onTouch listener as u have said. As far as I know listeners can only be added to Views. And also its not like you are adding onTouch Listener to the canvas of the onDraw() method u are basically applying to the whole view a view cannot have two same listeners.
I have created a custom view to be shown in the action bar. It mostly works except sometimes on start I see a mirror copy of whatever I draw. The copy overlaps the original one but a few pixels away.
My onDraw() override is quite simple. All it does is draws an image that is centered in the canvas.
I am a bit confused. Am I expected to clear the canvas first in onDraw() method? Thank you in advance for your help.
It is confusing, but you'll notice in the custom view samples (that come with the sdk), the onDraw() method first calls canvas.drawColor() before anything else.
I assume it's not done automatically because it would be wasteful in the case where what you were drawing filled the entire view anyway. I just wish it was more clear that it is necessary in most cases.
Here is what I found out that others may find useful. If you set a background using setBackground(), the framework will actually draw the background before calling onDraw(). This background is drawn in the center. I too was drawing the background in onDraw() but I was drawing it from top-left. Hence, I was seeing the ghost image.
As Krylez mentioned, you may wish to call drawColor() as your first call in onDraw().
What I did was overrode setBackground() and its variations, and stored the background bitmap in a local variable.
Regards,
Peter
Should I take view translation into account when overriding onDraw()? Or it is already applied to canvas translation?
I.e. should I execute
canvas.translate(getTranslationX(), getTranslationY());
in first line of onDraw()?
Do all transformations applied to canvas in onDraw() call persist in subsequent calls? I.e. should I suppose canvas is each time untransformed?
No, you do not need to do this. A View's translation is applied before onDraw is called.
As HardCoder points out, state changes that you make to the Canvas passed to onDraw will not persist to the next call to onDraw.
As far as I know the canvas is not persistant therefore you should execute the translate. However you can save the canvas and restore it:
http://maohao.wordpress.com/2009/09/30/canvas-save-canvas-restore/
Android View.onDraw() always has a clean Canvas
http://blahti.wordpress.com/2010/12/20/moving-views-in-android-part-1/
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'm creating a custom TextView by using TextPaint in the onDraw() method.
however this is causing severe problems for my app.
performance wise I mean.
The onDraw() method gets called over and over again .
but i just want it to draw once :(
i tried using setDrawingCacheEnabled(true); but no effect there.
There is also a viewflipper with textviews in the main layout rotating automaticly,
could this be causing everything to redraw ?
can anyone give me some pointers here ?
As well as enabling the drawing cache, try creating a new bitmap instance in the inDraw and pass that to the Canvas to draw to. In the next call to onDraw, simply draw that bitmap if it is not null. I'm doing this myself and it works a charm to improve performance, though unable to copy the code right now (out using phone).
As for why it is being consistently redrawn, are there any moving overlapping graphics causing it to be invalidated?