I'm trying to use the PorterDuff library to trim a canvas circle and rectangle to form a quarter of a square in my custom view for my app, I managed to get it to work but not fully because it trims the square out but keeps the rest of the circle in, I'm using the SRC_IN mode which seems like the right mode to use from looking at the android documentation for it but it's not working as expected, this is a snippet of the onDraw method in my custom view class:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height = getHeight();
int width = getWidth();
canvas.drawCircle(width / 2, height / 2, (width + height) / 10, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
Rect rectangle = new Rect(0, 0, width / 2, height / 2);
paint.setColor(Color.RED);
canvas.drawRect(rectangle, paint);
}
I'm drawing the circle in the center of the screen and then drawing the square in the top left of the screen and the PorterDuff mode should basically get the intersected part between the shapes but it just trims non-intersected square part out but doesn't do the same for the circle.
This is what it looks like:
I can't tell what i'm doing wrong here, hopefully someone can point it out.
the issue is with the source, the rectangle is the source in this context, draw the rectangle first then use PorterDuff.Mode.SRC_IN to cut the circle based on it.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height = getHeight();
int width = getWidth();
Rect rectangle = new Rect(0, 0, width / 2, height / 2);
paint.setColor(Color.RED);
canvas.drawRect(rectangle, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawCircle(width / 2, height / 2, (width + height) / 10, paint);
}
I have two lines where one line is drawn at the same point every time and the other line is rotated by a certain degree every time the view is invalidated.
The code is as shown below :
protected void onDraw(Canvas canvas) {
int width = getWidth();
int height = getHeight();
int centerx = width/2;
int centery = height/2;
canvas.drawLine(centerx, 0, centerx, height, paint);
canvas.drawLine(0, centery, width, centery, paint);
if (angle_in_degrees!= null)
canvas.rotate(angle_in_degrees, centerx, centery);
paint.setColor(0xff0000ff);
canvas.drawLine(centerx, -1000, centerx, +1000, paint);
canvas.drawLine(-1000, centery, 1000, centery, paint);
paint.setColor(0xff00ff00);
}
Everything works fine, but I want to fill the space between the two lines by a different color. How do I do that? Thanks.
The accepted answer to this question sounds as if it would apply just as well to your situation. Essentially, the author uses a Path object to connect the dots between all points that need to be filled, before finally called canvas.drawPath() to fill the area inside.
In your case, this would probably look something like:
Path path = new Path();
path.lineTo(centerX, 0);
path.lineTo(centerX, height);
path.lineTo(0, centerY);
//etc. Essentially mirroring what your line does, but in one dimension (x,y) points.
canvas.drawPath(path, paint);
Edit: You would also need to make sure the Paint object you use has a FILL style:
paint.setStyle(Paint.Style.FILL);
public static Bitmap drawCircle(int width,int height, int borderWidth) {
Bitmap canvasBitmap = Bitmap.createBitmap( width, height, Bitmap.Config.ARGB_8888);
BitmapShader shader = new BitmapShader(canvasBitmap, TileMode.CLAMP,
TileMode.CLAMP);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);
paint.setShader(null);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(borderWidth);
Canvas canvas = new Canvas(canvasBitmap);
float radius = width > height ? ((float) height) / 2f : ((float) width) / 2f;
canvas.drawCircle(width / 2, height / 2, radius - borderWidth / 2, paint);
return canvasBitmap;
}
Simple this code draws a circle with white border, however I want part of the border to be black and the other part white. 40 % of it black, 60 % of it white
How can this be done?
Try this code
class MyView extends View
{
private Paint paint;
public MyView(Context context, int x, int y)
{
super(context);
paint = new Paint();
// PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
paint.setAlpha(255);
// paint.setXfermode(xfermode);
paint.setAntiAlias(true);
// setBackgroundColor(Color.BLACK);
}
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawCircle(100, 100, 50, paint);
}
}
Here a utility method for filling a circle with one color and stroking the circle border with another color.
Use the second method to pass in an existing Paint instance, e.g. to set the anti-alias flag or to prevent memory allocations during onDraw().
public static void fillCircleStrokeBorder(
Canvas c, float cx, float cy, float radius,
int circleColor, float borderWidth, int borderColor) {
fillCircleStrokeBorder(c, cx, cy, radius, circleColor, borderWidth, borderColor, new Paint());
}
public static void fillCircleStrokeBorder(
Canvas c, float cx, float cy, float radius,
int circleColor, float borderWidth, int borderColor, Paint p) {
int saveColor = p.getColor();
p.setColor(circleColor);
Paint.Style saveStyle = p.getStyle();
p.setStyle(Paint.Style.FILL);
c.drawCircle(cx, cy, radius, p);
if (borderWidth > 0) {
p.setColor(borderColor);
p.setStyle(Paint.Style.STROKE);
float saveStrokeWidth = p.getStrokeWidth();
p.setStrokeWidth(borderWidth);
c.drawCircle(cx, cy, radius - (borderWidth / 2), p);
p.setStrokeWidth(saveStrokeWidth);
}
p.setColor(saveColor);
p.setStyle(saveStyle);
}
Just draw your circle at full size with one color, then draw the circles again at the same coordinate, but with a different color and with a smaller radius, and scale down the radius however much you need to, that would be the most trivial way to do it.
how to make that text was written vertically? how to rotate text 90 degrees?
Write each letter individually is stupid, but now ,i don't know another way.
Paint paint = new Paint();
public DrawView(Context context, double arr[])
{
super(context);
paint.setColor(Color.BLACK);
}
#Override
public void onDraw(Canvas canvas)
{
canvas.drawText("Test",50, 50, paint);
}
Simply rotating text (or anything else) is easy: Use the rotate() method to rotate the canvas (afterwards it is rotated back, otherwise everything you draw becomes rotated):
canvas.save();
canvas.rotate(90f, 50, 50);
canvas.drawText("Text",50, 50, paint);
canvas.restore();
The save() and restore()methods respectively save the state of the canvas and restores it. So the rest of your drawn elements are not rotated. If you only want to paint the text these two methods are not necessary.
If you want to put the characters of the string under each other, you need to process each character separately. First you'd need to obtain the font height and when drawing each character you need to increase the y-coordinate with this height over and over again.
int y = 50;
int fontHeight = 12; // I am (currently) too lazy to properly request the fontHeight and it does not matter for this example :P
for(char c: "Text".toCharArray()) {
canvas.drawText(c, 50, y, paint);
y += fontHeight;
}
Correct version is :
Canvas canvas_front = new Canvas(bitmap_front);
Paint paint = new Paint();
paint.setColor(Color.rgb(140, 0, 0));
paint.setAlpha(80);
paint.setStrokeWidth(2);
canvas_front.drawLine(0, (float) (frontIV.getHeight() * 0.9),frontIV.getWidth(), (float) (frontIV.getHeight() * 0.9), paint);
canvas_front.save();
canvas_front.rotate((float) 90 , 50, 50);
canvas_front.drawText("Text",50, 50, paint);
canvas_front.restore();
frontIV.setImageBitmap(bitmap_front);
I have two points in the canvas, now I'm able to draw a line between those points like this below image by using
This code canvas.drawLine(p1.x, p1.y, p2.x, p2.y, paint);
I want to draw the arc between two points like below image.
How can I draw like this.
Finally I got the solution from this code:
float radius = 20;
final RectF oval = new RectF();
oval.set(point1.x - radius, point1.y - radius, point1.x + radius, point1.y+ radius);
Path myPath = new Path();
myPath.arcTo(oval, startAngle, -(float) sweepAngle, true);
To calculate startAngle, use this code:
int startAngle = (int) (180 / Math.PI * Math.atan2(point.y - point1.y, point.x - point1.x));
Here, point1 means where you want to start drawing the Arc. sweepAngle means the angle between two lines. We have to calculate that by using two points like the blue points in my Question image.
Do something like this:
//Initialized paint on a class level object.
Paint p = new Paint();
p.setColor(Color.BLACK);
//Calculate the rect / bounds of oval
RectF rectF = new RectF(50, 20, 100, 80);
#Override
protected void onDraw(Canvas canvas) {
//Do the drawing in onDraw() method of View.
canvas.drawArc (rectF, 90, 45, false, p);
}
first we need to visual how the coordinates are in terms of start and sweep angels then it will become more clear.
so if you wanted just the right top piece of the circle, we could do something like this:
val rect = RectF(0f, 0f, 500f, 300f)
val paint = Paint()
paint.apply {
strokeWidth = 5f
setStyle(Paint.Style.STROKE)
color = COLOR.BLUE
}
path.addArc(rect, 270f, 90f)
..
this starts at 270 (per the diagram above and 'sweeps` 90 degrees forward. you then have this shape:
let's create one more so you get the hang of it. this time let's use a negative value: we want to create a semi half moon (arc) starting from the right side:
path.addArc(rect, 0f, -180f)
here we started at 0 and 'sweeped` -180 degrees.
and the results are:
I was trying to do something a little different and it's all about calculating sweep and start angles.
I wanted to show an arc that represents progress on a circle that goes from top to bottom.
So I had progress value from 0...100 and I want to show an arc that start from top to bottom to fill the circle when the progress is 100.
To calculate the sweepAngle I use:
int sweepAngle = (int) (360 * (getProgress() / 100.f));
Next is to calculate the startAngle
int startAngle = 270 - sweepAngle / 2;
Start Angle is calculated this way because:
It's always going to start from the left side, starting from the top to bottom. So starting angle at the top equals 270 (Note that it goes clockwise and 0 = 3 o'clock, so 12 o'clock equals 270 degrees)
Next I want to calculate how far I'm going to get away from my starting point (270) and to do that I only calculate half of the sweep angle because only half of the arc will be on the left side and the other half on the right side.
So considering I have progress of 25%
sweepAngle = 90 degrees (90 degrees is quarter of a circle)
start angle = 225 (45 degrees away from 270)
If you want the progress to go from other sides (Left to right, right to left etc..) you will only need to replace 270 with the starting the angle.
I may be late to answer but I got more information.
After Android Lollipop there are two ways to address this problem
public void drawArc(RectF oval, float startAngle, float sweepAngle,
boolean useCenter, Paint paint)
public void drawArc(float left, float top, float right, float bottom,
float startAngle, float sweepAngle, boolean useCenter, Paint paint)
Usage:
RectF rectF = new RectF(left, top, right, bottom);
// method 1
canvas.drawArc (rectF, 90, 45, true, paints[0]);
// method 2
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawArc (left, top, right, bottom, 0, 45, true, paints[1]);
}
Sweep angle is nothing more than angle of Sector which is drawn clockwise eg. for below code
private void drawArcs(Canvas canvas) {
RectF rectF = new RectF(left, top, right, bottom);
// white arc
canvas.drawArc (rectF, 90, 45, true, paints[0]);
// Green arc
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawArc (left, top, right, bottom, 0, 45, true, paints[1]);
}
// Red stroked arc
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawArc (left, top, right, bottom, 180, 45, true, paints[2]);
}
}
Result will look like this
Same can be achieved with the help of defining Paths and then iterating over them in onDraw method as illustrated in this snippet:
public class ArcDrawable extends Drawable {
private int left, right, top, bottom;
private Paint[] paints = new Paint[3];
private HashMap<Path, Paint> pathMap = new HashMap();
public ArcDrawable() {
// white paint
Paint whitePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
whitePaint.setColor(Color.WHITE);
paints[0]= whitePaint;
// green paint
Paint greenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
greenPaint.setColor(Color.GREEN);
paints[1]= greenPaint;
// red paint
Paint redPaint =new Paint(Paint.ANTI_ALIAS_FLAG);
redPaint.setColor(Color.RED);
redPaint.setStyle(Paint.Style.STROKE);
paints[2]= redPaint;
}
#Override
public void draw(Canvas canvas) {
//----------USE PATHS----------
// Define and use custom Path
for (Map.Entry<Path, Paint> entry : pathMap.entrySet()) {
// Draw Path on respective Paint style
canvas.drawPath(entry.getKey(), entry.getValue());
}
// -------OR use conventional Style---------
//drawArcs(canvas);
}
//Same result
private void drawArcs(Canvas canvas) {
RectF rectF = new RectF(left, top, right, bottom);
// method 1
canvas.drawArc (rectF, 90, 45, true, paints[0]);
// method 2
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawArc (left, top, right, bottom, 0, 45, true, paints[1]);
}
// method two with stroke
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawArc (left, top, right, bottom, 180, 45, true, paints[2]);
}
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
int width = bounds.width();
int height = bounds.height();
left = bounds.left;
right = bounds.right;
top = bounds.top;
bottom = bounds.bottom;
final int size = Math.min(width, height);
final int centerX = bounds.left + (width / 2);
final int centerY = bounds.top + (height / 2);
pathMap.clear();
//update pathmap using new bounds
recreatePathMap(size, centerX, centerY);
invalidateSelf();
}
private Path recreatePathMap(int size, int centerX, int centerY) {
RectF rectF = new RectF(left, top, right, bottom);
// first arc
Path arcPath = new Path();
arcPath.moveTo(centerX,centerY);
arcPath.arcTo (rectF, 90, 45);
arcPath.close();
// add to draw Map
pathMap.put(arcPath, paints[0]);
//second arc
arcPath = new Path();
arcPath.moveTo(centerX,centerY);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
arcPath.arcTo (rectF, 0, 45);
}
arcPath.close();
// add to draw Map
pathMap.put(arcPath, paints[1]);
// third arc
arcPath = new Path();
arcPath.moveTo(centerX,centerY);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
arcPath.arcTo (rectF, 180, 45);
}
arcPath.close();
// add to draw Map
pathMap.put(arcPath, paints[2]);
return arcPath;
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(#Nullable ColorFilter colorFilter) {
}
#Override
public int getOpacity() {
return 0;
}
}
Complete source code:
https://github.com/hiteshsahu/Arc-Drawable
a sample for draw arc.
public static Bitmap clipRoundedCorner(Bitmap bitmap, float r, boolean tr, boolean tl, boolean bl, boolean br)
{
int W = bitmap.getWidth();
int H = bitmap.getHeight();
if (r < 0)
r = 0;
int smallLeg = W;
if(H < W )
smallLeg = H;
if (r > smallLeg)
r = smallLeg / 2;
float lineStop = r/2;
Path path = new Path();
path.moveTo(0,0);
if(tr)
{
path.moveTo(0, lineStop);
path.arcTo(new RectF(0,0, r,r), 180, 90, false);
}
path.lineTo(W-lineStop, 0);
if(tl)
path.arcTo(new RectF(W-r,0, W,r), 270, 90, false);
else
path.lineTo(W, 0);
path.lineTo(W, H-lineStop);
if(bl)
path.arcTo(new RectF(W-r,H-r, W,H), 0, 90, false);
else
path.lineTo(W, H);
path.lineTo(lineStop, H);
if(br)
path.arcTo(new RectF(0,H-r, r,H), 90, 90, false);
else
path.lineTo(0,H);
if(tr)
path.lineTo(0,lineStop);
else
path.lineTo(0,0);
Bitmap output = Bitmap.createBitmap(W, H, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
canvas.drawPath(path, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, 0, 0, paint);
return output;
}
A simple solution was suggested here by Langkiller. This draws a cubic line from the start point via the control point to the end point.
Path path = new Path();
float startX = 0;
float startY = 2;
float controlX = 2;
float controlY = 4;
float endX = 4
float endY = 2
conePath.cubicTo(startX, startY, controlX, controlY,endX, endY);
Paint paint = new Paint();
paint.setARGB(200, 62, 90, 177);
paint.setStyle(Paint.Style.FILL);
canvas.drawPath(path, paint)