On Android how do I make oddly shaped clipping areas? - android

Here is how to create a clipping area the shape of a circle:
Path path = new Path();
path.addCircle(200,200,100,Direction.CW);
c.clipPath(path); // c is a Canvas
Now there's a clipping area on the Canvas which prevents drawing anything outside the bounds of that circle. But, what if I want to have the clipping area be shaped like a donut (or whatever)?
I tried playing around with creating a second Path and using toggleInverseFillType on it and then adding that to the original path, but that doesn't seem to work.
Alternatively, instead of using a Path, is it possible to just create a Bitmap to use as a mask and set it as a clipping mask on the Canvas somehow?
EDIT: The answer is exactly what I needed with one small addition. When doing multiple operations on a canvas, always use Op.REPLACE on the first clipPath call. That will replace any existing clipPath on that Canvas.
For reference, here is what I discovered what the 6 different Region.Op values mean. Imagine a venn diagram with 2 circles. "B" is the part where the 2 circles overlap. "A" is the non-overlapping left circle. "C" is the non-overlapping right circle.
c.clipPath(a,Region.Op.REPLACE);
c.clipPath(b,???);
Region.Op.DIFFERENCE -> A..
Region.Op.INTERSECT -> .B.
Region.Op.REPLACE -> .BC
Region.Op.REVERSE_DIFFERENCE -> ..C
Region.Op.UNION -> ABC
Region.Op.XOR -> A.C
The "." indicates the part that isn't drawn. Sorry if that's not particularly clear. It's hard to describe well without graphics.

From the Canvas javadoc:
Canvas#clipPath(Path path, Region.Op op) - Modify the current clip with the specified path.
So, for your donut example:
Create 2 Paths. One for the larger circle, one for the smaller circle.
Canvas#clipPath( Path ) with larger circle Path.
Call the Canvas#clipPath( Path, Region.Op ) method on your canvas with the smaller circle Path for the first argument and the appropriate Region.Op enum value for the second argument.
Path largePath = new Path();
largePath.addCircle(200,200,100,Direction.CW);
Path smallPath = new Path();
smallPath.addCircle(200,200,40,Direction.CW);
c.clipPath(largePath); // c is a Canvas
c.clipPath(smallPath, Region.Op.DIFFERENCE);
Again, modify the Region.Op enum value to get different effects...

Related

detecting intersection between Circle and Line (not closed Path)

I draw a line on the screen with an Android Path. -> drawPath. This path is NOT CLOSED !. So this is not a region.
I have a circle on the screen -> drawCircle.
How can I detect if the circle is touching the line ?
There are answers to similar question, but with closed path. There I could simple intersect regions. But how can we do this in case of a NOT CLOSED Path ?
Any hints ?
Try this dumb solution and optimize it if you don't find any other solution:
Create an empty bitmap
Create a canvas using that bitmap.
Draw circle in that canvas with red color.
Traverse the pixels in the bitmap and store the indices of pixel which contain red pixel.
Clear and draw that path in the canvas with black color.
Traverse all the pixels in the bitmap and if the pixel is black and check if that index exists in the indices of the circle.
If any index matches, then they overlap.

Is android Region is always a rectangular area or it may be polygonal or curvy?

Does Android Region (android.graphics.Region) always have a rectangular area or can it be polygonal or rounded (curvy)?
Actually I have to do some Region.Op.UNION and Region.Op.INTERSECTION operation with multiple regions.
I want to know the shape of Ultimate Output Region, does it still have a rectangular area or not?
It can be complex (isComplex()), i.e. it consists of more than one rectangle. Not sure what do you mean by "curvy", but it can be polygonal. If I understand it correctly, you can use getBoundaryPath() to get the Path describing resulting shape.
Nothing in the documentation would lead one to conclude that a Region can be anything but a rectangle, it being constructed from either a rectangle, an x,y coordinate plus width and height, or by another region.
One can describe a rectangle from a path, so getBoundaryPath() does not necessarily conclude that a non-rectangle is possible. An encompassing rectangular boundary may instead be implied.
The isComplex() property only says that it consists of multiple rectangles. Are they all bound by a single exterior, defining rectangle? If so, how do we separate them? In the absence of sufficient documentation, one cannot tell without experimentation:
The following code describes a path and creates a polygonal region. We start with an array of any number of coordinate pairs. Then:
//describe a path corresponding to the transformed polygon
Path transformPath;
transformPath = new Path();
//starting point
transformPath.moveTo(getTransformedPolygon()[0], getTransformedPolygon()[1]);
//draw a line from one point to the next
for(int i = 2; i < arrayCoordinates.length; i = i + 2)
{
transformPath.lineTo(arrayCoordinates[i], arrayCoordinates[i + 1]);
}
//then end at the starting point to close the polygon
transformPath.lineTo(arrayCoordinates[0], arrayCoordinates[1]);
//describe a region (clip area) corresponding to the game area (my example is a game app)
Region clip = new Region(0, 0, gameSurfaceWidth, gameSurfaceHeight);
//describe a region corresponding to the transformed polygon path
transformRegion = new Region();
transformRegion.setPath(transformPath, clip);
If you display the region as a string, you will see the several pair of coordinates that make up the polygonal shape.

layout like spider

I want a cylindrical, spider web like layout:
I know that I can use canvas to draw this but I also need all portions to be clickable, and canvas is very hard to handle touch for all portion.
Ideas?
can i want layout like spider...
Yes you can want it. But if you want to actually create that layout then you cannot do it with the standard android widgets.
If you want to make it then I would suggest drawing it on a Canvas manually and using the onTouchListener to catch the key presses.
I am not sure but i hope this can help you ...
The Path class holds a set of vector-drawing commands such as lines,
rectangles, and curves. Here’s an example that defines a circular path:
circle = new Path();
circle.addCircle(150, 150, 100, Direction.CW);
This defines a circle at position x=150, y=150, with a radius of 100
pixels. Now that we’ve defined the path, let’s use it to draw the circle’s
outline plus some text around the inside:
private static final String QUOTE = "Now is the time for all " +
"good men to come to the aid of their country." ;
canvas.drawPath(circle, cPaint);
canvas.drawTextOnPath(QUOTE, circle, 0, 20, tPaint);
You can see the result in this Figure
If you want to get really fancy, Android provides a number of PathEffect
classes that let you do things such as apply a random permutation to a
path, cause all the line segments along a path to be smoothed out with
curves or broken up into segments, and create other effects.

Dynamically create / draw images to put in android view

I'm not sure I'm doing this the "right" way, so I'm open to other options as well. Here's what I'm trying to accomplish:
I want a view which contains a graph. The graph should be dynamically created by the app itself. The graph should be zoom-able, and will probably start out larger than the screen (800x600 or so)
I'm planning on starting out simple, just a scatter plot. Eventually, I want a scatter plot with a fit line and error bars with axis that stay on the screen while the graph is zoomed ... so that probably means three images overlaid with zoom functions tied together.
I've already built a view that can take a drawable, can use focused pinch-zoom and drag, can auto-scale images, can switch images dynamically, and takes images larger than the screen. Tying the images together shouldn't be an issue.
I can't, however, figure out how to dynamically draw simple images.
For instance: Do I get a BitMap object and draw on it pixel by pixel? I wanted to work with some of the ShapeDrawables, but it seems they can only draw a shape onto a canvas ... how then do I get a bitmap of all those shapes into my view? Or alternately, do I have to dynamically redraw /all/ of the image I want to portray in the "onDraw" routine of my view every time it moves or zooms?
I think the "perfect" solution would be to use the ShapeDrawable (or something like it to draw lines and label them) to draw the axis with the onDraw method of the view ... keep them current and at the right level ... then overlay a pre-produced image of the data points / fit curve / etc that can be zoomed and moved. That should be possible with white set to an alpha on the graph image.
PS: The graph image shouldn't actually /change/ while on the view. It's just zooming and being dragged. The axis will probably actually change with movement. So pre-producing the graph before (or immediately upon) entering the view would be optimal. But I've also noticed that scaling works really well with vector images ... which also sounds appropriate (rather than a bitmap?).
So I'm looking for some general guidance. Tried reading up on the BitMap, ShapeDrawable, Drawable, etc classes and just can't seem to find the right fit. That makes me think I'm barking up the wrong tree and someone with some more experience can point me in the right direction. Hopefully I didn't waste my time building the zoom-able view I put together yesterday :).
First off, it is never a waste of time writing code if you learned something from it. :-)
There is unfortunately still no support for drawing vector images in Android. So bitmap is what you get.
I think the bit you are missing is that you can create a Canvas any time you want to draw on a bitmap. You don't have to wait for onDraw to give you one.
So at some point (from onCreate, when data changes etc), create your own Bitmap of whatever size you want.
Here is some psuedo code (not tested)
Bitmap mGraph;
void init() {
// look at Bitmap.Config to determine config type
mGraph = new Bitmap(width, height, config);
Canvas c = new Canvas(mybits);
// use Canvas draw routines to draw your graph
}
// Then in onDraw you can draw to the on screen Canvas from your bitmap.
protected void onDraw(Canvas canvas) {
Rect dstRect = new Rect(0,0,viewWidth, viewHeight);
Rect sourceRect = new Rect();
// do something creative here to pick the source rect from your graph bitmap
// based on zoom and pan
sourceRect.set(10,10,100,100);
// draw to the screen
canvas.drawBitmap(mGraph, sourceRect, dstRect, graphPaint);
}
Hope that helps a bit.

Android: how to combine the paths of two shapes and remove overlap?

I want to create a speech balloon type shape, where there is a rectangle or ellipse with a triangular shape jutting out of it.
How I'm attempting to do this is to create a Path object that combines the triangle with the other shape (round rect).
I'm doing it as follows:
Path path = new Path();
// Create triangular segment
Point drawOffset = getAttributes().getDrawOffset();
int leaderGap = getAttributes().getLeaderGapWidth();
// Recall that we have a coordinate system where (0,0) is the
// bottom midpoint of the annotation rectangle.
// the point to left of gap
int x1 = -leaderGap/2;
int y1 = 0;
// the point to right of gap
int x2 = leaderGap/2;
int y2 = 0;
// The point where we're drawing to; the end of the pointy segment of triangle
int x3 = -drawOffset.x;
int y3 = drawOffset.y;
path.moveTo(x2, y2);
path.lineTo(x3, y3);
path.lineTo(x1, y1);
// path.close();
// Add the rectangular portion to the path
path.addRoundRect(backgroundShape, 5, 5, Path.Direction.CW);
The problem is that the roundRect is a closed path, so its edge shows through underneath the triangular section.
A picture's worth a thousand words, so here you go:
What I want is for the line segment between those two endpoints of the triangle to disappear, so that it looks like one seamless path.
If all I were doing were a straight rectangle, I could create the whole path myself from scratch. But I'd like to do the rounded corners, and it'd be a bit of a paint to do that with the Path (yes I know you can do quad to and arcTo but it's still not as clean a solution as I'd like).
So in general, is it possible to combine two paths and create a single union object that traces the perimeter of both?
Yes this is possible as of API 19. You can perform logical operations between 2 paths. As in your case, you can create a UNION of the two paths, using Path.op(). For a complete list of the operations that can be performed refer here.
I can't see a convenient way to combine paths in this manner. I would normally achieve this by manually drawing the rounded rectangular parts with Path.arcTo() and Path.lineTo(), it's a little extra effort but it will achieve the result you are looking for.
Another option which is perhaps more flexible if you decide to change the theme is to use a ninepatch drawable there's even an editor to let you create them called Draw 9-patch

Categories

Resources