I want to draw texts on the sides of a circle and another one below it. I edited the code in this answer but the problem is that the circle and arc are taking the whole space of the rect, which makes no space for the text to be drawn.
Here is my code
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// getHeight() is not reliable, use getMeasuredHeight() on first run:
// Note: mRect will also be null after a configuration change,
// so in this case the new measured height and width values will be used:
if (mRect == null) {
// take the minimum of width and height here to be on he safe side:
centerX = getMeasuredWidth() / 2;
centerY = getMeasuredHeight() / 2;
radius = Math.min(centerX, centerY);
// mRect will define the drawing space for drawArc()
// We have to take into account the STROKE_WIDTH with drawArc() as well as drawCircle():
// circles as well as arcs are drawn 50% outside of the bounds defined by the radius (radius for arcs is calculated from the rectangle mRect).
// So if mRect is too large, the lines will not fit into the View
int startTop = STROKE_WIDTH / 2;
int startLeft = startTop;
int endBottom = 2 * radius - startTop;
int endRight = endBottom;
mRect = new RectF(startTop, startLeft, endRight, endBottom);
}
// subtract half the stroke width from radius so the blue circle fits inside the View
canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH / 2, mBasePaint);
// Or draw arc from degree 192 to degree 90 like this ( 258 = (360 - 192) + 90:
// canvas.drawArc(mRect, 192, 258, false, mBasePaint);
// draw an arc from 90 degrees to 192 degrees (102 = 192 - 90)
// Note that these degrees are not like mathematical degrees:
// they are mirrored along the y-axis and so incremented clockwise (zero degrees is always on the right hand side of the x-axis)
canvas.drawArc(mRect, 270, mTemp * 6, false, mDegreesPaint); // Each degree in the temp scale = 6 degrees on circle
canvas.drawArc(mRect, 270 + mSeparator * 6, 3, false, mSeparatorPaint); // The separator size = 3 degrees
// subtract stroke width from radius so the white circle does not cover the blue circle/ arc
canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH, mCenterPaint);
drawCenter(canvas, mTextPaint, mTemp + "°");
canvas.drawText("Temp 1", mRect.centerX(), radius * 2, mTextPaint);
}
This code produces what I need as an arc and circles, but I'm not able to draw degrees on the circle sides and a text below it
Any help??
Related
I'm playing with custom views and I want to build a rounded rectangle using path.lineTo() and path.arcTo() methods.
So, the rectangle I want to get:
Normally I draw this with this block of code:
RectF backReftf = new RectF();
Path path = new Path();
int width = getWidth();
int height = getHeight();
float curve = (float) (0.1 * height);
RectF backReftf = new RectF();
backReftf.left = 0;
backReftf.top = 0;
backReftf.right = width;
backReftf.bottom = height;
path.addRoundRect(backReftf, curve, curve, Path.Direction.CW);
canvas.drawPath(path, paint);
But I want to draw this with path.lineTo() and path.arcTo().
According to Docs about arcTo():
Append the specified arc to the path as a new contour. If the start of the path is different from the path's current last point, then an automatic lineTo() is added to connect the current contour to the start of the arc. However, if the path is empty, then we call moveTo() with the first point of the arc.
So theoretically my arc should start there, where line ends, so if I drew a line (left side of rectangle):
float curve = (float) (0.1 * height);
path.moveTo(0,0);
path.lineTo(0, height - curve);
then my arc should start from this point (0, height - curve), but where do I pass those arguments when arcTo() has following parameters: arcTo (float left,
float top,
float right,
float bottom,
float startAngle,
float sweepAngle,
boolean forceMoveTo)
?
Plus how do I calculate in this case startAngle and sweepAngle?
Thanks in advance!
When drawing an arc, you need to specify the full bounding box for that arc, and the start and sweep angle. I try to see them visually, like so:
E.g. when going clock wise, the start angle is positioned 180degrees from the origin. And from the startAngle, if you sweep 90 degrees clock wise, you'll end up at the desired end position.
Take note where the origin, startAngle and sweepAngle are in this graphic.
In kotlin, it can look something like this:
// Given some radius, viewWidth and viewHeight
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
path.apply {
moveTo(radius, 0F)
lineTo(viewWidth - radius, 0F)
arcTo(viewWidth - 2 * radius, 0F, viewWidth, 2 * radius, -90F, 90F, false)
lineTo(viewWidth, radius)
arcTo(viewWidth - 2 * radius, viewHeight - 2 * radius, viewWidth, viewHeight, 0F, 90F, false)
lineTo(radius, viewHeight)
arcTo(0F, viewHeight - 2 * radius, 2 * radius, viewHeight, 90F, 90F, false)
lineTo(0F, radius)
arcTo(0F, 0F, 2 * radius, 2 * radius, 180F, 90F, false)
}
canvas?.drawPath(path, linePaint)
}
And the result will be something like this:
First I will explain what I am trying to achieve. I am trying to achieve cropping functionality. For that, I am drawing one rectangle on canvas and then trying to draw heart inside rectangle. So If user minimize/maximize rectangle heart will also minimize/maximize.
Here is my code.
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mIsInitialized) {
setMatrix();
Matrix localMatrix1 = new Matrix();
localMatrix1.postConcat(this.mMatrix);
Bitmap bm = getBitmap();
if (bm != null) {
canvas.drawBitmap(bm, localMatrix1, mPaintBitmap);
// draw edit frame
drawEditFrame(canvas);
}
}
}
private void drawEditFrame(Canvas canvas) {
mPaintTransparent.setFilterBitmap(true);
mPaintTransparent.setColor(mOverlayColor);
mPaintTransparent.setStyle(Paint.Style.FILL);
Path path = new Path();
path.addRect(mImageRect.left, mImageRect.top, mImageRect.right, mImageRect.bottom,
Path.Direction.CW);
drawHeart(mFrameRect.centerX(), mFrameRect.centerY(), path);
canvas.drawPath(path, mPaintTransparent);
}
private Path drawHeart(float width, float height, Path path) {
// Starting point
path.moveTo(width / 2, height / 5);
// Upper left path
path.cubicTo(5 * width / 14, 0,
0, height / 15,
width / 28, 2 * height / 5);
// Lower left path
path.cubicTo(width / 14, 2 * height / 3,
3 * width / 7, 5 * height / 6,
width / 2, height);
// Lower right path
path.cubicTo(4 * width / 7, 5 * height / 6,
13 * width / 14, 2 * height / 3,
27 * width / 28, 2 * height / 5);
// Upper right path
path.cubicTo(width, height / 15,
9 * width / 14, 0,
width / 2, height / 5);
return path;
}
private void setMatrix() {
mMatrix.reset();
mMatrix.setTranslate(mCenter.x - mImgWidth * 0.5f, mCenter.y - mImgHeight * 0.5f);
mMatrix.postScale(mScale, mScale, mCenter.x, mCenter.y);
mMatrix.postRotate(mAngle, mCenter.x, mCenter.y);
}
But heart is sticking at top left corner and not moving or minimizing/maximizing as per rectangle.
Here is current image for reference.
Please provide me hint or any reference.
EDIT I am able to draw circle inside rectangle which minimize/maximize as per rectangle size and move as per rectangle move. But I am not able to do same with custom heart. Here is code for circle.
path.addCircle((mFrameRect.left + mFrameRect.right) / 2,
(mFrameRect.top + mFrameRect.bottom) / 2,
(mFrameRect.right - mFrameRect.left) / 2, Path.Direction.CCW);
Here is image in which I am drawing circle in rectangle which I want to achieve same with heart too.
The implementation be like you should retake the whole bitmap size(height, width) before resizing then max/min the size of that bitmap.
I have a custom view where I override the onDraw method and drawn a circle. Now I want to draw a line from the center of the circle to the top of the circle.
Here is my code..
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint);
canvas.drawCircle(centerX, centerY, outerRadius, outerCirclePaint);
double angleRadians = Math.toRadians(0);
double x = (outerRadius * Math.cos(angleRadians)) + centerX;
double y = (outerRadius * Math.sin(angleRadians)) + centerY;
canvas.drawLine((float)x, (float)y, centerX, centerY, innerCirclePaint);
}
centerX and centerY are the center of the circle
outerRadius is the radius for the circle
When i run this the line is drawn from the center to the right at 90 degrees instead of the top of the circle 0 degrees, even though I have told it the angle is 0
This is confusing me and can't seem what I am doing wrong.
If anyone has any ideas on this I would be very grateful
In mathematics, 0 degrees (also 0 radians) for the unit circle start at the right of the circle.
Unit circle
Add 90 degrees or pi/2 radians to start at the top.
I'm trying to draw an arc inside a circle to represent the temperature, but I'm having a difficult time achieving that. During my search I found those solutions
This one I couldn't understand what is the scale method for, and is drawing more than one arch, which confused me a bit
This post gave it a fixed size where I need the size to be controlled by the custom view in my XML layout
From here I understood the concept the degrees but I didn't understand how to determine the oval size
What i reached so far
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int startTop = 0;
int startLeft = 0;
int endBottom = getHeight() / 2;
int endRight = endBottom;// This makes an equal square.
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
int upperEdgeX = (int) (centerX + getWidth() / 2 * Math.cos(270 * Math.PI / 180));
int upperEdgeY = (int) (centerY + getWidth() / 2 * Math.sin(270 * Math.PI / 180));
int bottomEdgeX = (int) (centerX + getWidth() / 2 * Math.cos(90 * Math.PI / 180));
int bottomEdgeY = (int) (centerY + getWidth() / 2 * Math.sin(90 * Math.PI / 180));
int leftEdgeX = (int) (centerX + getWidth() / 2 * Math.cos(180 * Math.PI / 180));
int leftEdgeY = (int) (centerY + getWidth() / 2 * Math.sin(180 * Math.PI / 180));
int rightEdgeX = (int) (centerX + getWidth() / 2 * Math.cos(0 * Math.PI / 180));
int rightEdgeY = (int) (centerY + getWidth() / 2 * Math.sin(0 * Math.PI / 180));
RectF rect = new RectF(startTop, startLeft, endRight, endBottom);
canvas.drawCircle(centerX, centerY, getWidth() / 2, mBasePaint);
canvas.drawCircle(centerX, centerY, getWidth() / 3, mCenterPaint); // White circle
}
UPDATE:
I need my view to be like a Donut Pie chart where the middle will hold the degree
UPDATE 2:
I'm trying to have something like this
The following custom View draws two arcs connecting to form a circle as well as an inner circle.
Moreover, I let it fill the rectangle used for drawing the arc and use a yellow background for the View, the activity background is dark. (These additional features are meant for getting a better impression of how drawing a circle / an arc works, they can help with debugging.)
The custom View:
public class MySimpleView extends View{
private static final int STROKE_WIDTH = 20;
private Paint mBasePaint, mDegreesPaint, mCenterPaint, mRectPaint;
private RectF mRect;
private int centerX, centerY, radius;
public MySimpleView(Context context) {
super(context);
init();
}
public MySimpleView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MySimpleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init()
{
mRectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mRectPaint.setColor(ContextCompat.getColor(getContext(), R.color.magenta));
mRectPaint.setStyle(Paint.Style.FILL);
mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCenterPaint.setColor(ContextCompat.getColor(getContext(), R.color.white));
mCenterPaint.setStyle(Paint.Style.FILL);
mBasePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBasePaint.setStyle(Paint.Style.STROKE);
mBasePaint.setStrokeWidth(STROKE_WIDTH);
mBasePaint.setColor(ContextCompat.getColor(getContext(), R.color.blue));
mDegreesPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mDegreesPaint.setStyle(Paint.Style.STROKE);
mDegreesPaint.setStrokeWidth(STROKE_WIDTH);
mDegreesPaint.setColor(ContextCompat.getColor(getContext(), R.color.green));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// getHeight() is not reliable, use getMeasuredHeight() on first run:
// Note: mRect will also be null after a configuration change,
// so in this case the new measured height and width values will be used:
if (mRect == null)
{
// take the minimum of width and height here to be on he safe side:
centerX = getMeasuredWidth()/ 2;
centerY = getMeasuredHeight()/ 2;
radius = Math.min(centerX,centerY);
// mRect will define the drawing space for drawArc()
// We have to take into account the STROKE_WIDTH with drawArc() as well as drawCircle():
// circles as well as arcs are drawn 50% outside of the bounds defined by the radius (radius for arcs is calculated from the rectangle mRect).
// So if mRect is too large, the lines will not fit into the View
int startTop = STROKE_WIDTH / 2;
int startLeft = startTop;
int endBottom = 2 * radius - startTop;
int endRight = endBottom;
mRect = new RectF(startTop, startLeft, endRight, endBottom);
}
// just to show the rectangle bounds:
canvas.drawRect(mRect, mRectPaint);
// subtract half the stroke width from radius so the blue circle fits inside the View
canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH / 2, mBasePaint);
// Or draw arc from degree 192 to degree 90 like this ( 258 = (360 - 192) + 90:
// canvas.drawArc(mRect, 192, 258, false, mBasePaint);
// draw an arc from 90 degrees to 192 degrees (102 = 192 - 90)
// Note that these degrees are not like mathematical degrees:
// they are mirrored along the y-axis and so incremented clockwise (zero degrees is always on the right hand side of the x-axis)
canvas.drawArc(mRect, 90, 102, false, mDegreesPaint);
// subtract stroke width from radius so the white circle does not cover the blue circle/ arc
canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH, mCenterPaint);
}
}
I have following code:
protected void onDraw(Canvas canvas)
{
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
xRatio = getWidth()*1.0f / picWidth;
yRatio = getHeight()*1.0f / picHeight;
canvas.drawBitmap( sourceImage, null , new Rect(0,0,getWidth(),getHeight()),paint);
for (int i = 0; i < eyesMidPts.length; i++)
{
if (eyesMidPts[i] != null)
{
// where x and y are eyes mid point coordinates
float x = eyesMidPts[i].x*xRatio;
float y = eyesMidPts[i].y*yRatio;
float radius = (float) (eyesDistance[i]);
float left = x - radius;
float right = x + radius;
float top = y - radius;
// we want to increase the bottom radius by double to get other half of the face.
float bottom = (float) (y + radius * 2);
paint.setStrokeWidth(eyesDistance[i] /20);
RectF ovalBounds = new RectF();
ovalBounds.set(left, top, right, bottom);
canvas.drawOval(ovalBounds, paint);
}
}
}
Code encompass full face if face is straight. But does not get complete circle around if face is tilted. I am not sure how euler angels work but am hoping it is to get tilt on the face detection. Can someone please help me with this show me some example code so that circle encompasses whole face.
Why do you use RectF to draw a circle? Is it a 'true' circle, or is it really oval / ellipse?
If it's just a circle, why don't you use this.
canvas.drawCircle(x, y, radius, paint);
So you won't need to transform Rectf's points if it's tilted :)
update:
I believe this should work, I don't have an android workflow setup with me to really test it; sorry if it's a miss.
Matrix m = new Matrix(); //creates identity matrix
m.setRotate(tilt_angle); //rotate it by the tilt angle
m.mapRect(ovalBounds); //transform the rect
// RectF face: face position and dimentions
// Create Bitmap where to draw an oval
Bitmap ovalBmp = Bitmap.createBitmap( face.width(), face.height(), config );
canvasBmp = new Canvas(ovalBmp); // Get a canvas to draw an oval on the Bitmap
canvasBmp.drawOval( new RectF( 0, 0, face.width(), face.height ), paint );
// Create transformation matrix
transforMatrix = new Matrix();
// Rotate around center of oval
transforMatrix.postRotate( tilt_angle, face.width() / 2, face.height() / 2 );
transforMatrix.postTranslate( face.left, face.top );
canvas.drawBitmap( ovalBmp, transforMatrix, null );
ps: I assume by tilt angle you mean roll, where pitch is rotation around X axis, yaw is rotation around Y axis and roll is rotation around Z axis.