I have a fullscreen custom view in my activity where I have to draw paths that are formed by a line each one. Since my view supports scaling, depending on how zoomed in the view is, the paths get to big and therefore the view becomes slow, even crashing sometimes. What I want to know is if there's some built-in function where I can draw only the part of the path that is actually inside the canvas.
I've tried using Path.op with a rect the size of the screen, but didn't get any results; it either returns an empty path or a path that contours the screen. Same thing with Canvas.clipPath(). Passing a rect on through invalidate() also doesn't work for me since the said path IS insterecting the drawing area.
It's also worth metioning that I'm using paths because I need to use a PathDashPath effect. So, if there's a way os doing that with drawLine, it'd also help.
Related
I am wondering if there is a way to draw filled areas (like a filled polygon) with the Android Canvas without using the Path class and call canvas.drawPath(...).
The reason I want to do this without Path is because I have to draw very large datasets and canvas.drawPath(...) is not hardware accelerated and therefore slow.
The performance when using canvas.drawLines(...) is much better because of hardware acceleration, however I have not found a way to draw the polygon filled using this approach (even when the lines are all connected).
Even calling paint.setStyle(Style.FILL) did not fill the polygon when using drawLines(...).
Is there a way to draw a filled-polygon without using the Path approach?
Or is there any other way to improve performance using the Canvas?
You might want to look at opengl view and use it for all the drawings you need. Definitely will be damn fast. Still, all your drawing code needs to be re-written.
You probably need to do something like :
Paint red = new Paint();
red.setColor(android.graphics.Color.RED);
red.setStyle(Paint.Style.FILL);
And use this color for your path, instead of your ARGB. Make sure the last point of your path ends on the first one, it makes sense also.
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.
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 am following the below tutorial:
http://www3.ntu.edu.sg/home/ehchua/programming/android/Android_2D.html
In it,
Canvas canvas.drawOval();
is called in order to have a default circle drawn, that bounces around. My normal way of learning a new graphics framework is to build on this, and then upgrade to images. Normally, this is very simple, but I"m having trouble here. There is no equivalent "drawImage" to the drawOval command (which I'm more used to).
Instead, I'm trying to figure out "drawables".
So, following another tutorial (specifically the default Android "snake" game), I tried doing:
Resources r = this.getContext().getResources();
in my view, then passed the resource object to my ball object, to get:
img = r.getDrawable(R.drawable.ball);
where ball is a file stored in /res/drawable/ball.png
In the ball objects draw method (which I call in the view's onDraw method), I have:
img.draw(canvas);
canvas is passed from onDraw, which is itself passed a canvas. I don't really understand who is calling onDraw (I assume some internal process), but this differs from the snake example where they make their own canvas?
Either way, this doesn't seem to work. I am not getting any image, or any errors. I know that img is at least populated (its not null or anything), but other than that I don't really know what to do...
If this were Ruby (my most familiar language), I'd call inspect on the img to see if actually has anything in it...is there an equivalent for Java (or should I fool around with break points)?
Is there anything obvious that I'm doing wrong? Can I not use the default canvas I'm being passed, even though I clearly can for drawing simple shapes?
Ah, I figured it out:
With the draw oval method, I needed to set the bounds like so:
RectF bounds.set(ballX-ballRadius, ballY-ballRadius, ballX+ballRadius, ballY+ballRadius);
But for the drawable object, I have to go one step further and say
img.setBounds(bounds);
and make bounds be a Rect instead of a RectF.
Once that is done, voila, things are rendering.
It didn't occur to me at first that the bounds are how things know where to render themselves, and while you pass the bounds to an oval, you have to SET them to a drawable.
The problem is pretty complicated to explain but here goes:
I'm making a paint program that draws paths onto a canvas with textured background. Each stroke is stored as a path that updates as the user moves the stylus across the screen. When the path is updated I call drawpath on the canvas. The problem is that on each move event, the path is drawn over the existing line on the canvas, so the antialiasing on it darkens the existing line and make it appear thicker and jaggier than expected.
I had a solution where I store the older canvas (the one without the active path) and keep another transparent canvas on top of that. I would clear the top canvas and redraw the path on each move event, and then draw both canvases together. BUT that makes the program so slow that the paths look terrible - you can tell the drawing is lagging way behind the stylus movements.
Is there any way to make either A) drawing / clearing multiple canvases faster or B) make antialiasing not mess up on multiple redraws?
Figured out.
It was so simple I can't believe I got stuck on it.
The "canvas" used in onDraw() is automatically erased every time, so I just called canvas.drawPath() with the currently updating path in the onDraw() function, at no extra cost.