Circle drawn on canvas doesn't match the screen - android

I want to draw circle in center of screen, but I'm getting something like this:
I'm using this code to draw this circle.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
Canvas c = new Canvas(bmp);
RectF rect = new RectF(0,0,width,width);
drawCircle(rect, c, width, height);
ImageView img = (ImageView) findViewById(R.id.imageView1);
img.setImageBitmap(bmp);
img.setScaleType(ScaleType.FIT_CENTER);
}
private void drawCircle(RectF rect, Canvas c, int width, int height) {
Paint paint = new Paint();
paint.setARGB(255, 255 , 10, 21);
paint.setStrokeWidth(10);
paint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.BUTT);
paint.setStyle(Paint.Style.STROKE);
int radius;
if(width < height)
radius = width/2;
else
radius = height/2;
c.drawCircle(width/2, height/2, radius, paint);
}
I don't understand why it's cut at sides even though I use size of screen to draw it, so it should perfectly fit it.

You didn't account for the thickness of the line (strokeWidth). You drew a circle assuming it had 0 thickness, so the "actual" circle IS touching the edges of the screen, but since you used a thick paintbrush, some of the paint leaked past the edge.

you should decrease the thickness/2.
private void drawCircle(RectF rect, Canvas c, int width, int height) {
Paint paint = new Paint();
paint.setARGB(255, 255 , 10, 21);
paint.setStrokeWidth(10);
paint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.BUTT);
paint.setStyle(Paint.Style.STROKE);
int radius;
if(width < height)
radius = width/2;
else
radius = height/2;
//this is the new line:
radius-= 5;
c.drawCircle(width/2, height/2, radius, paint);
}

Account for the StrokeWidth in the radius:
// Substract stroke width.
radius -= paint.getStrokeWidth() / 2;
c.drawCircle(width/2, height/2, radius, paint);

Related

How to add transparent background on Canvas text in android?

I want to add transparent canvas on the price quote. Exactly we can see in following picture ($74). I tried to follow SO question but nothing was working for me. Here is my code:
#Override
public Bitmap transform(Bitmap bitmap) {
// TODO Auto-generated method stub
synchronized (ImageTransform.class) {
if (bitmap == null) {
return null;
}
Bitmap resultBitmap = bitmap.copy(bitmap.getConfig(), true);
Bitmap bitmapImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_heart);
bitmapImage = Bitmap.createScaledBitmap(bitmapImage, 50, 50, true);
Canvas canvas = new Canvas(resultBitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(40);
paint.setShadowLayer(2.0f, 1.0f, 1.0f, Color.BLACK);
canvas.drawText("$250", 20, 400, paint);
canvas.drawBitmap(bitmapImage, 510, 55, null);
bitmap.recycle();
return resultBitmap;
}
How can I add a transparent color or image on price section?
Edit-1
However I am able to add canvas rectangle but not at the right position
Paint paintrect = new Paint();
paintrect.setColor(Color.BLACK);
paintrect.setStyle(Paint.Style.FILL_AND_STROKE);
paintrect.setStrokeWidth(10);
float left = 20;
float top = 80;
float right = 50;
float bottom = 0;
canvas.drawRect(left, top, right, bottom, paintrect);
I got this :
You can see black rectangle on top-left of image.
You are almost there!
Paint paintrect = new Paint();
paintrect.setColor(Color.BLACK);
paintrect.setStyle(Paint.Style.FILL_AND_STROKE);
paintrect.setStrokeWidth(10);
paintrect.setAlpha(127);
float left = 18;
float top = 360;
float right = 128;
float bottom = 416;
canvas.drawRect(left, top, right, bottom, paintrect);
You will have to tweak the rectangle coordinates to get it right where you want; I estimated as best I could.

Draw circle with different border colors Android

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.

Rotate text when drawing it on a bitmap

I want to rotate my text by 180 degrees when drawing it on a bitmap. The bitmap itself could also be rotated as there is nothing else drawn on it other than the text. It isn't clear to me though what I should be using in the code below to rotate the text: ImageView, canvas, paint, bitmap???
ImageView ivImage = (ImageView)findViewById(R.id.ivImage);
DisplayMetrics metrics = getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
Bitmap.Config conf = Bitmap.Config.ARGB_8888;
Bitmap bitmap = Bitmap.createBitmap(width, height, conf);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.RED);
paint.setTextSize((int) (200 * 1));
// draw text to the Canvas center
Rect bounds = new Rect();
paint.setTextAlign(Align.CENTER);
String text = "HELP";
paint.getTextBounds(text, 0, text.length(), bounds);
int x = bitmap.getWidth() / 2; // (bitmap.getWidth() - bounds.width())/2;
int y = bitmap.getHeight() / 2; // (bitmap.getHeight() + bounds.height())/2;
canvas.drawText(text, x * 1, y * 1, paint);
ivImage.setImageBitmap(bitmap);
I hope this could help. This is how i did when creating a clock
// Save canvas in current state
canvas.save();
// Rotate canvas around center and draw
canvas.rotate(degrees, canvasWidth/2, canvasHeigth/2);
canvas.drawText(text, x, y, paint)
// Restore canvas, rotates it back to original
canvas.restore();

Android - Cut a circle from a square Bitmap

I am trying to cut a circle from a square bitmap using following code
Canvas canvas=new Canvas(bitmapimg );
int circleXCoord = bitmapimg .getWidth() / 2;
int circleYCoord = bitmapimg .getHeight() / 2;
int circleRadius = bitmapimg .getWidth() / 2;
Rect rect = new Rect(circleXCoord - circleRadius, circleYCoord - circleRadius, circleXCoord + circleRadius, circleYCoord + circleRadius);
int width = rect.width();
int height = rect.height();
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.BLUE);
canvas.drawRect(rect, paint);
canvas.drawBitmap(bitmapimg , rect, rect, paint);
Path p = new Path();
p.addCircle(circleXCoord, circleYCoord, width / 2F, Path.Direction.CW);
canvas.clipPath(p, Region.Op.DIFFERENCE);
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
The idea is to attach a square (rectangular) bitmap to canvas and then clip a circular path. Clear out the difference between the rectangle and circle (make it transparent).
The code works fine for Android 4, but on Android 2.3.3 device, the difference area is appearing black rather that transparent.
Am I missing something here or PorterDuff.Mode.CLEAR is not supported in gingerbread? Is there a better way to cut a circle from a square in Android?
Seems like PorterDuff.Mode.Clear did not work for gingerbread
Solved the problem (cut circle from square using this code)
public Bitmap BitmapCircularCroper(Bitmap bitmapimg){
Bitmap output = Bitmap.createBitmap(bitmapimg.getWidth(),
bitmapimg.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmapimg.getWidth(),
bitmapimg.getHeight());
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawCircle(bitmapimg.getWidth() / 2,
bitmapimg.getHeight() / 2, bitmapimg.getWidth() / 2, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmapimg, rect, rect, paint);
return output;
}
import android.graphics.PorterDuff.Mode;
import android.graphics.Bitmap.Config;
public static Bitmap getCircularBitmap(Bitmap bitmap)
{
Bitmap output;
if (bitmap.getWidth() > bitmap.getHeight()) {
output = Bitmap.createBitmap(bitmap.getHeight(), bitmap.getHeight(), Config.ARGB_8888);
} else {
output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getWidth(), Config.ARGB_8888);
}
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
float r = 0;
if (bitmap.getWidth() > bitmap.getHeight()) {
r = bitmap.getHeight() / 2;
} else {
r = bitmap.getWidth() / 2;
}
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawCircle(r, r, r, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
For anyone that's still looking at this, this answer will cause a few issues.
1.) The canvas that you are creating in this instance will not have hardware acceleration. Even though your Paint object has anti-aliasing, the canvas will not. This will cause artifacting when you decide to paint this back to your original canvas in your onDraw() call.
2.) This takes a lot more resources. You have to create a second Bitmap (which can cause OOM), and a secondary Canvas as well as all of the different alterations you have to do.
Please check out Romain Guy's answer. You create a BitmapShader and then create a RoundRect that gives you a Circle. You just need to know the dimensions of your RectF so that it can determine the circle properly.
This means that if you know the center point (x, y) and radius, you can easily determine the RectF.
left = x - radius;
top = y - radius;
right = x + radius;
bottom = y + radius;
This also means that with this solution posted below you only have to draw to the screen once, everything else is done in the off-screen buffer.
http://www.curious-creature.com/2012/12/11/android-recipe-1-image-with-rounded-corners/
The best solution is found here:
BitmapShader shader;
shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);
RectF rect = new RectF(0.0f, 0.0f, width, height);
// rect contains the bounds of the shape
// radius is the radius in pixels of the rounded corners
// paint contains the shader that will texture the shape
canvas.drawRoundRect(rect, radius, radius, paint);

How to draw Arc between two points on the Canvas?

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)

Categories

Resources