I'm attempting to draw a rectangle around a custom view in Android. I mostly have it working except for one detail.
Here is my code...
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStrokeWidth(14.5f);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRoundRect(0, 0, getWidth(), getHeight(), 20.0f, 20.0f, paint);
And here is the resulting rectangle...
As you can see, the inside of the rectangle does have rounded corners, but the outside still draws pointed corners. How can I make it so that the outside corners are rounded as well?
You don't see corners rounded on the outside because part of the stroke is outside of the bounds of the Canvas. You can check this easily by adding a certain margin to the coordinates of the round rectangle to make sure it is drawn inside the Canvas.
In fact your best option is trying to optimize this margin depending on the selected stroke width.
Related
I am trying to draw a heart shaped Canvas using Path in Android. The code is as follows :
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Fill the canvas with background color
canvas.drawColor(Color.WHITE);
// paint.setShader(null);
// Defining of the heart path starts
path.moveTo(left + WIDTH / 2, top + HEIGHT / 4); // Starting point
// Create a cubic Bezier cubic left path
path.cubicTo(left+WIDTH/5,top,
left+WIDTH/4,top+4*HEIGHT/5,
left+WIDTH/2, top+HEIGHT);
// This is right Bezier cubic path
path.cubicTo(left + 3 * WIDTH / 4, top + 4 * HEIGHT / 5,
left + 4 * WIDTH / 5, top,
left + WIDTH / 2, top + HEIGHT / 4);
paint.setShader(new LinearGradient(0, canvas.getHeight()/4, canvas.getWidth(), canvas.getHeight()/4, new int[]{Color.RED, Color.YELLOW, Color.GREEN}, new float[]{0, 0.6f, 1}, Shader.TileMode.CLAMP));
canvas.drawPath(path, paint);
heart_outline_paint.setColor(getResources().getColor(R.color.heart_outline_color)); // Change the boundary color
heart_outline_paint.setStrokeWidth(4);
heart_outline_paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(path, heart_outline_paint);
}
I am able to draw heart without any issue and I am able to fill color inside the heart using the Fill option in Paint. But I should be able to fill the heart dynamically according to some data and it cannot be filled fully all the time. What I have achieved so far is as follows :
I have made an extensive search and came across a lot of things similar to this. Some of which includes :
Android fill in part of a path?
filling a circle gradually from bottom to top android
I also came across the concept of converting the canvas to bitmap and filling color inside the bitmap using Flood Fill Algorithm which lets users to fill colors inside the bitmap. However, I do not want the bitmap to fill the color while touching inside the heart but to fill while a button click action.
I thought that filling a circle gradually from bottom to top android
would give help me but it makes use of a circle and I am not well-versed in Canvas which makes me very weak in adapting the circle code to such a shape.
If anybody has some ideas or any insights on how to achieve this, it will be really helpful. Cheers. Thanks in advance.
P.S : I also tried some tricks using setShader in Paint but nothing would give me what I want.
EDIT :
I just stumbled upon a idea of drawing a rectangle over the heart with another color same as the background of the canvas so that it will look like its half filled !! I am still working on the idea and not sure how accurate this is gonna be for me. If someone has a better idea, you're most welcome.
I used clipPath function available in Canvas to achieve what I needed. I draw the heart by above method and draw a rectangle over it, and I use the clipPathfunction to clip out the region that is outside the heart.
public static double filled_amount = .90;
path.moveTo(left_x_moveto, left_y_moveto);
path.cubicTo(left_x1, left_y1, left_x2, left_y2, left_x3, left_y3);
path.cubicTo(right_x2, right_y2, right_x1, right_y1, left_x_moveto, left_y_moveto);
path.close();
Rect rect = new Rect((int)(canvas.getWidth()*.10),(int)(canvas.getHeight()*filled_amount),(int) canvas.getWidth(), (int) canvas.getHeight());
canvas.clipPath(path);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
canvas.drawPath(path, paint);
canvas.drawRect(rect, rect_paint);
heart_outline_paint.setColor(getResources().getColor(R.color.heart_outline_color)); // Change the boundary color
heart_outline_paint.setStrokeWidth(15);
heart_outline_paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(path, heart_outline_paint);
This will give me the desired result of filling the heart dynamically. Changing the value of filled_amount dynamically and calling invalidate() will make it look like the heart is being filled dynamically.
#Henry's answer might be a better one but this did the trick for me and I dont look deeply in to the edges so a bit of zig-zags here and there is all right.
You could use Bitmap Masking to get a partially filled Heart. What you ideally do here is use one bitmap to mask the other.
In your case you could have a filled rectangle in the canvas and you have then have the heart shape in a new bitmap to act as the mask. You could then dynamically change the filling of the heart by changing the height of the background rectangle.
Refer this: https://stackoverflow.com/a/33483600/4747587. This contains the implementation of partially filling a Star. The idea is the same.
I am trying to create a Black screen with a transparent Hole in the middle of the screen. Here is what i have tried.
#Override
public void draw(Canvas canvas)
{
Paint myPaint = new Paint();
myPaint.setColor(0xC0000000);
canvas.drawRect(mBlackRect, myPaint);
myPaint = new Paint();
myPaint.setColor(Color.TRANSPARENT);
myPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRect(mTransparentRect, myPaint);
}
The second paint, shows black color instead of transparent. How can i punch a transparent hole in MY SemiBlack Canvas?
you didn't save the canvas, try the code below
Paint myPaint = new Paint();
int sc = canvas.saveLayer(mBlackRect.left, mBlackRect.top,
mBlackRect.right, mBlackRect.bottom, myPaint,
Canvas.ALL_SAVE_FLAG);
myPaint.setColor(0xC0000000);
canvas.drawRect(mBlackRect, myPaint);
myPaint.setColor(Color.TRANSPARENT);
myPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRect(mTransparentRect, myPaint);
myPaint.setXfermode(null);
canvas.restoreToCount(sc);
You can not really "punch" a hole by "removing pixels" from something already drawn, at least not with a hardware layer. And if you use a software layer, it will be bad for performance.
What you want to do is draw your shape with an alpha mask applied to your paint. A mask will prevent some parts of the shape to be drawn on the canvas, like cutting a piece of paper and stick it on a wall before spreading the painting.
To apply an alpha mask to your paint, you first need to create a bitmap containing the "hole" shape (programmatically or by loading a custom image from resources), then create a BitmapShader from this bitmap with the proper Xfermode (depending if you want the transparent part in your mask bitmap to be cut out or the non-transparent part) and finally apply this shader to your paint before drawing the semitransparent rectangle or anything you want.
Be careful with performance: only create the Paint object once (do not allocate any object in onDraw() because this method gets called up to 60 times per second on the UI thread), and recreate the alpha mask bitmap only when the bounds of your View/Drawable change (if its dimensions depend on the View dimensions of course, otherwise you just create it once).
I'm sorry if I don't have time to give you ready-to-use code but I think you should find plenty of information about the technique I just described and you can start experimenting and figuring out the solution by yourself which is more rewarding I think ;)
I can't just seem to figure it out. I am trying to draw a segmented circle (what looks like circle inside a circle). However I want the segments to have specific colors and to be transparent inside the smaller circle. Preferably , I would like to make the color of the segmented lines different than the circle
Here are the solutions I had in mind:
1- Draw arc with fill color for the bigger circle and draw a circle for the small circle. 2 problems with this. First one is that the inner circle area is no longer transparent as it takes the color from the bigger one. Second problem is that the segmentation lines of the outer circle is going all the way to the center (not only to the inner circle perimeter)
2) Draw arcs for the bigger outer circle and draw circle for the inner circle. Set it to be color filled but don't show strokes. Then draw another outer circle on top with no fill just to show strokes. And then draw lines between the inner and outer circle using the calculations ( angle and radius) to determine where the lines are... Very convoluted solution, there has to be another way. Even with this solution, still have problem with the color showing in the center but maybe playing with gradient can help.
I read so much on SO but I couldn't figure the right answer as many answers would remove the control of circle parameters
HEELP!!!
#Override
public void draw(Canvas canvas) {
float size = Math.min(getWidth(),getHeight());
paint.setStrokeWidth(size/4);
paint.setStyle(Paint.Style.STROKE);
final RectF oval = new RectF(0, 0, getWidth(), getHeight());
oval.inset(size/8,size/8);
paint.setColor(Color.RED);
Path redPath = new Path();
redPath.arcTo(oval, 0, 120, true);
canvas.drawPath(redPath, paint);
paint.setColor(Color.GREEN);
Path greenPath = new Path();
greenPath.arcTo(oval, 120, 120, true);
canvas.drawPath(greenPath, paint);
paint.setColor(Color.BLUE);
Path bluePath = new Path();
bluePath.arcTo(oval, 240, 120, true);
canvas.drawPath(bluePath, paint);
paint.setStrokeWidth(2);
paint.setColor(0xff000000);
canvas.save();
for(int i=0;i<360;i+=40){
canvas.rotate(40,size/2,size/2);
canvas.drawLine(size*3/4,size/2,size,size/2,paint);
}
canvas.restore();
final RectF ovalOuter = new RectF(0, 0, getWidth(), getHeight());
ovalOuter.inset(1,1);
canvas.drawOval(ovalOuter,paint);
final RectF ovalInner = new RectF(size/4, size/4, size*3/4,size*3/4);
canvas.drawOval(ovalInner,paint);
}
I'm drawing arcs using the Path class and strokes. Style.STROKE gives arcs without filling. Stroke width is set to size/4 which is a quarter of the view. Half of that stroke width goes outside and the second half goes inside, like this:
xxxxxxxx outer border of the arc of width 5
xxxxxxxx
------------ stroke
xxxxxxxx
xxxxxxxx inner border of the arc
That's why I'm using insets - I need to offset the stroke a bit in order to fit it in the view. Without insets the arcs are cut by all four sides of the view.
And why canvas rotation? Because it's easier to rotate the canvas with built-in methods than calculate lines manually. Rotation uses trigonometric functions and quickly becomes quite complex, hard to read and error prone. Basically I'm rotating the paper and drawing straight lines.
Imagine that I have a rectangle image. How could I create a style like the next one?
I mean, cropping the image into a circle, add the border, the shadow and the gross /shine effect. Until now, I only have tried this snippet code to crop the image: Cropping circular area from bitmap in Android but just that. I have no idea how to do the remaining components in Android.
An easy way to achieve this effect is to use Canvas.drawCircle() and a BitmapShader:
BitmapShader s = new BitmapShader(myPhoto, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint p = new Paint();
p.setShader(s);
myCanvas.drawCircle(centerX, centerY, radius, p);
To do the shadow, simply call Paint.setShadowLayer() on the paint (this will only work if you draw the effect into an offscreen Bitmap or if your View uses a software layer – set by calling View.setLayerType() –).
The border can be drawn by drawing another circle on top, using the Paint.Style.STROKE style (that you can set by calling Paint.setStyle()).
Finally you can draw the gloss by drawing a circle, oval or Path on top of your very first circle. You'll need to use a LinearGradient shader on your paint and you'll also need to clip the gloss. You can do this in two ways:
If you are drawing the entire effect into a Bitmap, which is what I would recommend, simply set the paint's Xfermode to a new PorterDuffXfermode(PorterDuff.Mode.SRC_IN).
If you are drawing the effect directly on screen you can simply use Canvas.clipPath() to set a circular clip. Note that this will work with hardware acceleration only as of Android 4.3.
How do I draw a Rect that basically creates a hole in the canvas? For example, say I have an image editing application that's made for Pixel art. The user touches an area of the screen, and draws a purple rectangle. Oops, he put the rectangle in the wrong spot. The user needs to make this purple square turn transparent again so that he can re-draw it in the right position.
How do I do this? I've used PorterDuff.Mode.Clear, myPaintColor.setColor(Color.TRANSPARENT) etc. but all give the same result. An ugly black square where it should be transparent. My canvas starts out as transparent, and can be saved via getDrawingCache to a transparent .PNG, so I know the canvas starts off as transparent. But how do I help my user who drew the purple square to make his canvas transparent again? Thanks. I've been trying to figure this out all day, reading all the documentation on this, but I can't figure it out.
Here's what I have:
Rect square = new Rect();
Paint drawColor = new Paint();
//drawColor.setColor(UserColor.get(i));
drawColor.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
square.set(CanvasFractionWidth * UserX.get(i), CanvasFractionHeight * UserY.get(i), (CanvasFractionWidth * UserX.get(i)) + (CanvasFractionWidth), (CanvasFractionHeight * UserY.get(i)) + CanvasFractionHeight);
canvas.drawRect(square, drawColor);
In RoboDemo, I use this :
Paint p = new Paint();
p.setXfermode( new PorterDuffXfermode( Mode.CLEAR ) );