I know how to draw paths on a canvas and understand how to undo/redo. But now I want to draw shapes (lines, circles, rectangles) that dynamically resize depending on how I drag them - just like we have in MS Paint.
If I use "drawLine", the line is there permanently with no way of erasing it and redrawing it to my new finger location. Same with circle as I want the circle to constantly change width as I drag my finger. So the old one has to erased (keeping all the other paths on the bitmap intact) and the new one drawn in its place.
Been searching a lot for this, but haven't come across how t do it. Maybe I'm using the wrong keywords, but I don't know. Any pointers?
Each time you move the finger, call the underlying view's invalidate() function, it will trigger erasing the entire background
public void invalidate ()
Since: API Level 1
Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future. This must be called from a UI thread. To call from a non-UI thread, call postInvalidate().
Then redraw your shape based on your finger's new position.
Managed to do it. I misunderstood the way the offscreen drawing thing worked. The idea is to write to the bitmap only after you have the shape you want - ie on "touch up". Till then, draw the shape using the "canvas" object in on Draw...
Related
I have an android app I have written, that basically has a custom overlay, in this overlay I have overwritten the DRAW method to draw something on a canvas and at the end of my DRAW override, I call the super DRAW passing in the canvas I have painted.. the result is, every time the map is modified in anyway my custom overlay is redrawn as expected.
Now, I am trying to accomplish the same thing on iPhone and am getting a little confused.
Basically I need something drawn in the top right corner of the map every time a redraw occurs of the map. The drawin will change change with every update as well so can't simply be put a view over the map and pass touches through etc.
So, I guess the question is, what is the euivalent of the DRAW method in iOS.. how would I roughly accomplish this same thing? There are MKOverlayView in the API, but there seem to be some significant differences.. So, how do I put something say at 10x10 over the map, whos size is variable and make sure every time the map is moved, scaled or otherwised interacted with this object is redrawn at location 10x10 on the screen.
The docs for MKMapView state that you should not subclass MKMapView, so you can't / shouldn't override the drawRect method. You could add a UIView, though, and override drawRect to do your custom drawing.
I am setting up a game loop. The loop is set to update at 50 FPS. Currently the only action the app does is draw screen coordinates on response to touch events. Originally I set up the draw function to only draw new touch events. This caused the text to blink. Multiple touch events blink at different times, which leads me to think that Canvas uses multiple pages behind the scenes. In an effort to get around this I made a list of every touch event that happened and on every draw call I redraw the entire list.
Please correct me if I am wrong, the lockCanvas()/unlockCanvasAndPost() calls are in essence a backbuffer swap. Reading the documentation on locking and unlocking it sounds like it is necessary to redraw the entire scene between calls unless you use lockCanvas(Rect dirty). In this case the dirty rect area needs to be redraw while the outside area is preserved.
The content of the Surface is never preserved between unlockCanvas()
and lockCanvas(), for this reason, every pixel within the Surface area
must be written. The only exception to this rule is when a dirty
rectangle is specified, in which case, non-dirty pixels will be
preserved.
I know it sounds like I've answered my own question, but according to the documentation the Surface is never preserved between calls. However this does not explain the behavior of my first implementation, which was I would tap the screen and the text would start blinking. Since I only drew the text one time, this would mean that the blinking is from swapping to the "backbuffer" which didn't get the drawText() call, and the original Surface which did not get destroyed but perhaps it is to be considered unreliable.
So, the question: Do I need to redraw all objects on each draw call? And if so, do I need to "clear" the canvas, or at the least redraw the background image also?
yes, unless you're doing a dirty redraw where you define the region that you are redrawing, every point on your canvas gets destroyed.
I want to be able to dynamically place image(s) over another image in my app.
Consider the first image as background and the other images to be on top level, I will also need to move those top level images (change their x and y on the screen) by code too.
Imagine, for example, a sea in which the user places fish and sea animals, then those sea animals start to move here and there on the screen: it will be like that.
How can I do this? If you don't know but remember any simple program or demo that does that, it will be also very welcome!
Thank you!
There is, of course, more than one way to do this, but I would say that the best way to do it would be to create a custom View (class that derives from View) and have this handle your bitmap drawing and all of your touch events.
There's a lot of code to write for loading the Bitmaps and keeping track of all of their positions (and then drawing them to the canvas in onDraw), but if you start really small by just allowing one image to be drawn and dragged around the screen, you can build on that and keep your code organized.
You would need to override onDraw(Canvas) and onTouchEvent(MotionEvent) in your custom View. You'll load your bitmaps with BitmapFactory (decodeResource method if you're including your images as resources in your project) and you'll need to remember to call recycle on your bitmaps when you're no longer using them.
In onDraw, you draw your bitmaps to the canvas at a specific location using Canvas.drawBitmap. There are two overloads of this method you can choose from, one that takes the top and left coordinates of the bitmap as floats (and therefore performs no scaling or stretching) and one that takes a destination and source rectangle to perform scaling, stretching and placement.
I always use the latter as it gives me finer tuned control. If you choose this route, you'll want to keep two Rect instances and a Bitmap instance for each image being drawn, update them in the touch events and draw them to the canvas in the draw event.
When something changes inside your view (as in the case of a touch event), call invalidate() method and the framework will know to redraw everything which triggers your onDraw method.
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 scrollview that contains a custom view. The custom view is bigger than the area of the screen and draws properly.
The scrollview however tends to call onDraw() non-stop when scrolling, and I can't seem to make it smooth.
I used ScrollView.getDrawingRect() to calculate the visible portion of the screen and only draw to that, but it still returns the entire viewport (so it's optimized to not draw offscreen areas), and not the delta between the last position and the current one. Ideally I'd want to draw only the delta, and not the entire visible window.
If anyone can point me to more information about how to use the drawing caches, and if that will help optimize scrolling, I'd love to implement it, or any other possible solutions it would be greatly appreciated.
When content is scrolled, the entire viewport needs to be redrawn because all of the content has moved. I don't think there's anything that needs to be done to optimize a ScrollView - if scrolling is slow then it's the drawing method of your custom view that is too slow.
Try to avoid object creation in your draw methods which is usually the main culprit for poor drawing performance.
Edit: Also the scrollview could blit the old content up or down quickly that is still drawn on the screen, and then request a redraw of only the "new" portion of the screen. (only applies to opaque views).
I encountered the same problem. I solved it by using the function setDrawingCacheEnabled(true). By enabling this setting, your canvas view will be cached as bitmap, so you don't have to call canvas' draw method each time onDraw() is called.
In your custom view's constructor, you will need something like this:
public CustomView(Context context) {
setDrawingCacheEnabled(true);
drawnFlag = false;
}
In your onDraw method, you will need something like this:
public void onDraw(Canvas canvas) {
if (! drawnFlag) {
canvas.drawPath(...);
canvas.drawPath(...);
drawnFlag = true;
}
}
Now, scrolling on this custom view should be smooth since we only call the drawing methods once.
Afaik the ScrollView sets a proper clip rect on the canvas your view gets in onDraw so you only need to draw what's inside that rect.
You could also implement cache bitmaps based on the clip rect's size.