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
Related
I have a lot of rectangles, described using the top-left coordinate, where x increases as it goes right and y increases as it goes down. I need to find out whether the rectangles intersect with each other.
I drew my rectangles using Inkscape, and I computed them with x and y coordinates (the top-left of the rectangle), height, width, and stroke-width from the SVG file so there were a lot of computations I've done prior to this method so I was kind of assuming that I have to set a threshold or something alike before I could actually see if they intersect (which explains why I placed 0.001 in my code, but it was just a guess/test value).
When I drew them, I placed their x,y coordinates and I don't think they intersect but I'm assuming that the stroke-width would have an effect on their x,y coordinates.
I thought I could use Java.Awt.Rectangle#intersects method in Android but apparently, I can't. I also cannot use android.graphics.Rect#intersects because I am dealing with double and it only allows integers and I also do not need it to be drawn on my screen.
I have tried to create my own code for this based on what I've read online but the output isn't as what I expected, some of it are actually correct but there were a few errors. Below is the code I've tried.
// x1_1, y1_1 = top-left coordinate of r1
// x1_2, y1_2 = top-right coordinate of r1
// x2_1, y2_1 = top-left coordinate of r2
// x2_2, y2_2 = top-right coordinate of r2
double a1 = Math.abs(x1_1-x2_2);
double a2 = Math.abs(x1_2-x2_1);
double b1 = Math.abs(y1_1 - y2_2);
double b2 = Math.abs(y1_2 - y2_1);
return !(a1>0.001 && a2>0.001 && b1>0.001 && b2>0.001);
Oftentimes, I could see that the x coordinates are indeed similar or close to each other, but based on the actual image, they are not intersecting. Here is a demonstration:
In this image, I drew a red line to describe that the two rectangles "kind of intersect" in terms of the code, which does not satisfy an actual intersection. I think it was the fact that I had to check the y-coordinates as well but I'm no longer sure of how to do that because I think I have to check a range of values.
I hope I could get some help as to see if a rectangle intersects another or is beside another rectangle. Perhaps using a library or a modification on my existing code because it is not working as it should. Thanks in advance!
You can use Rect to work with rectangles, and check intersection in particular:
Rect r1 = new Rect(left1, top1, right1, bottom1);
Rect r2 = new Rect(left2, top2, right2, bottom2);
boolean result = r1.intersect(r2);
If you need float coordinates, you can use RectF
I am using the 3d framework min3d and am trying to draw a rectangle that covers the whole screen. What parameters should I pass to the constructor of the Rectangle class?? It is declared this way:
public Rectangle(float $width, float $height, int $segsW, int $segsH, Color4 color)
I don't see what are segsH and segsW for. I can't see any doc about the constructor.
I got it, it didn't have anything to do with those parameters. By default the rectangle is drawn completely perpendicular to the screen so you can't see it. If you want to see it with the same dimensions you drew it you have to rotate it in y axis. Example:
rect = new Rectangle(1, 1, 2, 2,new Color(255,0,0,255));
rect.rotation().y = 180;
rect.lightingEnabled(false);
scene.addChild(rect);
In case this is usefull for someone else =)
It appears segsW and segsH control how many triangles are used to draw the rectangle.
I'm trying to create a 'glow' effect using the Android Path class. However, the gradient is not being warped to fit around the path. Instead, it is simply being display 'above' it and clipped to the path's stroke. Using a square path, the image below shows what I mean:
Instead, that should look more like this:
In other words, the gradient follows the path, and in particular wraps around the corners according to the radius set in the CornerPathEffect.
Here is the relevant part of the code:
paint = new Paint();
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(20);
paint.setAntiAlias(true);
LinearGradient gradient = new LinearGradient(30, 0, 50, 0,
new int[] {0x00000000, 0xFF0000FF, 0x00000000}, null, Shader.TileMode.MIRROR);
paint.setShader(gradient);
PathEffect cornerEffect = new CornerPathEffect(10);
paint.setPathEffect(cornerEffect);
canvas.drawPath(boxPath, paint);
Any ideas?
Another alternative is to get a 'soft-edged brush' effect when defining the stroke width. I've experimented with BlurMaskFilters, but those give a uniform blur rather than a transition from opaque to transparent. Does anyone know if that's possible?
How about drawing with a soft brush bitmap? Make a soft circular brush with opacity decreasing radially outward using image editing software like Photoshop. Save as drawable, load it in a bitmap and draw it evenly spaced along your path. Make the bitmap with white coloured brush. This way you can simply multiply the given colour(Here blue) to your bitmap using PorterDuffColorFilter.
brush1=BitmapFactory.decodeResource(getResources(), R.drawable.brush_custom_one);
//This contains radially decreasing opacity brush
porter_paint.setColorFilter(new PorterDuffColorFilter(paint.getColor(), Mode.MULTIPLY));
for (int i=1;i<matrix.size();i++) {
//matrix contains evenly spaced points along path
Point point = matrix.get(matrix.get(i));
canvas.drawBitmap(brush1, point.x,point.y, porter_paint);}
The brush used is (It's there):
The final result is:
Turns out there was a stupidly obvious way of doing this. Simply re-use the same path, and adjust the stroke width and alpha on each drawing pass. Example code:
float numberOfPasses = 20;
float maxWidth = 15;
for (float i = 0; i <= numberOfPasses; i++){
int alpha = (int) (i / numberOfPasses * 255f);
float width = maxWidth * (1 - i / numberOfPasses);
paint.setARGB(alpha, 0, 0, 255);
paint.setStrokeWidth(width);
canvas.drawPath(path, paint);
}
See below for an example of the result. The left path was drawn using this method, the right path, for comparison, is drawn in a single stroke with maxWidth and 255 alpha.
This mainly works. There are two problems:
The gradient isn't as smooth as it could be. This is because each pass being drawn over the previous one results in the alpha building up too quickly, reaching 255 before the final strokes. Experimenting a bit with the line int alpha = (int) (i / numberOfPasses * 125f); (note the change to 125f rather than 255f) helps.
The path looks like it has been 'cut' on the insides of the corners. Probably some result of the CornerPathEffect applied.
What you're wanting to do, if I understand it right, is to have the gradient effectively form a "brush" for the stroke.
This is exactly what I also was trying to achieve recently, but as far as I can tell the API doesn't provide any straightforward means to do it. I have recently created an SVG to Android Canvas converter class and so I am working a lot in Inkscape lately, too. So, when I was looking into it, I wondered if it's even possible to do it in Inkscape. However, even in Inkscape it's a very non-trivial thing to do. After some searching I eventually came across this image of a gradient being applied along the course of a path, together with a download link for a tutorial beneath:
http://www.flickr.com/photos/35772571#N03/3312087295/
What I was personally trying to do at the time was to create some semi-circles where the path is a kind of neon glow as opposed to a flat colour. Talking in terms of both the Android API and the SVG standard, it seems that the only way to to do this is to create a radial gradient that's centred perfectly on the circle, and position a series of color stops in exactly the right places. Pretty tricky to do, and I certainly don't know how you'd do it to a shape like a square.
Sorry that this is a bit of a 'I couldn't do it either' rather than a useful answer! I'll follow this with interest as I'm eager to know a solution for a kind of 'soft brush' effect too.
Can be very complicated to draw a gradient than follow a path.
So I suggest you to use some library already done than make it for you.
One can be Sc-Gauges.
Have some usefully classe than you can use for your goal.
For first include the library:
dependencies {
...
compile 'com.github.paroca72:sc-gauges:3.0.7'
}
After create an image or what you want with a canvas where draw:
<ImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Now the code:
// Dimensions
int padding = 24;
Rect drawArea = new Rect(padding, padding, 700 - padding, 500 - padding);
// Get the main layout
ImageView imageContainer = (ImageView) this.findViewById(R.id.image);
assert imageContainer != null;
// Create a bitmap and link a canvas
Bitmap bitmap = Bitmap.createBitmap(
drawArea.width() + padding * 2, drawArea.height() + padding * 2,
Bitmap.Config.ARGB_8888
);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.parseColor("#f5f5f5"));
// Create the path building a bezier curve from the left-top to the right-bottom angles of
// the drawing area.
Path path = new Path();
path.moveTo(drawArea.left, drawArea.top);
path.quadTo(drawArea.centerX(), drawArea.top, drawArea.centerX(), drawArea.centerY());
path.quadTo(drawArea.centerX(), drawArea.bottom, drawArea.right, drawArea.bottom);
// Feature
ScCopier copier = new ScCopier();
copier.setPath(path);
copier.setColors(Color.RED, Color.GREEN, Color.BLUE);
copier.setWidths(20);
copier.draw(canvas);
// Add the bitmap to the container
imageContainer.setImageBitmap(bitmap);
And this the result:
The first part of the code is just for create a bitmap where draw.
What you interest is the second part where use ScCopier.
Just give the path, the color and the with.
Note than is you are inside a view you can use onDraw for draw directly on the view canvas.
This library can used to create gauge of every kind.
If you want take a look to this site ScComponents have some free and not gauges components.
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.
I'm currently programming very simple game for Android (API level 7) to discover and learn the android SDK. This game involve drawing shape on the screen that will change colour when touched.
Some shapes may embed one or several holes. My issue is : if I touch the shape, the whole thing's colour change, even the holes'. Here is the pseudo code I use, shape is the polygon I want to draw, boundary it's outer boundary, holes an array of its holes. Hole and boundary hold an array of their points.
Path MyPath = Path();
Path.moveTo(boundary.points[0].x, boundary.point[0].x);
for (point in boundary) {
MyPath.lineTo(point.x, point.y);
}
Path.close();
for (hole in shape.holes) {
MyPath.moveTo(hole.points[0].x,hole.points[0].y);
for (point in hole) {
MyPath.lineTo(point.x, point.y);
}
MyPath.close();
}
// setting Paint here...
canvas.drawPath(MyPath, MyPaint);
Is their something I'm missing regarding Path in Android or do you have some alternative way to do it?
Are you sure you are using the correct path filling rule? If you are using for example WINDING as filling rule the holes must be in the opposite directions in respect to the outer boundary (e.g. border counter-clockwise and holes clockwise)