guys
I try to implement an black arrow which is on head of arc. The arrow animates with the arc, and has circle trajectory. I already implement the red arc with animation based on different values.
Please see the attachment.
How can I implement the black arrow on top of red arc? since if I do the same way as red arc animation, the black arrow will print the trajectory which is not desired.
Thanks in advance!
Leon
all you need is Canvas.
Here is example.
This is the your draw class:
public class CircleView extends View
{
public CircleView(Context context) {
super(context);
path = new Path();
paint = new Paint();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float angle = 270;
float radius = canvas.getWidth()/3;
float x = canvas.getWidth()/2;
float y = canvas.getHeight()/2;
final RectF oval = new RectF();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(25);
oval.set(x - radius, y - radius, x + radius,y + radius);
// Draw circle
paint.setColor(Color.RED);
canvas.drawArc(oval, 135, angle, false, paint);
paint.setColor(Color.BLUE);
canvas.drawArc(oval, angle, 405-angle, false, paint);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLACK);
float l = 1.2f;
float a = angle*(float)Math.PI/180;
// Draw arrow
path.moveTo(x+ (float)Math.cos(a) *radius, y + (float)Math.sin(a) * radius);
path.lineTo(x+ (float)Math.cos(a+0.1) *radius*l, y + (float)Math.sin(a+0.1) * radius*l);
path.lineTo(x+ (float)Math.cos(a-0.1) *radius*l, y + (float)Math.sin(a-0.1) * radius*l);
path.lineTo(x+ (float)Math.cos(a) *radius, y + (float)Math.sin(a) * radius);
canvas.drawPath(path, paint);
}
private Path path;
private Paint paint;
}
This is activity for it:
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new CircleView(this));
}
}
For angle == 200 you will see image like this:
Working copy for IntelliJ Idea: https://github.com/weerf/android_circle
Can someone explain exactly how rotation of the canvas about a point works ?
I have lines and I want to draw text parallel to each line. I have worked out the trigonometry required to calculate the angle of the line and it`s centre point.
When I try rotate the canvas about the start point of the line, then draw the text and restore, I am always getting strange offsets which obviously means I do not quiet get how the rotation is working ...
Can someone explain what happens when you rotate the canvas about a point and how the then drawing on co-ords X,Y translates back ?
see the following class:
class V extends View {
private Paint mPaint;
public V(Context context) {
super(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(0xffeeeeee);
mPaint.setTextSize(24);
}
#Override
protected void onDraw(Canvas canvas) {
float x = 100;
float y = 50;
float dx = 60;
float dy = 40;
canvas.drawLine(x, y, x + dx, y + dy, mPaint);
canvas.save();
float degrees = (float) (180 * Math.atan2(dy, dx) / Math.PI);
canvas.rotate(degrees, x, y);
canvas.drawText("text", x, y, mPaint);
canvas.restore();
}
}
now i hope everything should be clear how canvas rotation works...
I'm trying to animate drawing out a circle. In my custom view, I have
private final Paint mPaint = new Paint() {
{
setDither(true);
setStyle(Paint.Style.STROKE);
setStrokeCap(Paint.Cap.ROUND);
setStrokeJoin(Paint.Join.ROUND);
setColor(Color.BLUE);
setStrokeWidth(30.0f);
setAntiAlias(true);
}
};
...
protected void onDraw(Canvas canvas) {
super.onDraw();
if (mOval == null) {
mOval = new RectF(getLeft(), getTop(), getRight(), getBottom());
}
if (mPath == null) {
mPath = new Path();
mPath.moveTo(0, getHeight() / 2);
}
float sweepAngle = Math.min((float) mElapsedTime / 1000 * 60 * 1, 1) * 360;
if (sweepAngle == 0) {
mPath.reset();
} else if (mCurrentAngle != sweepAngle) {
mPath.arcTo(mOval, mCurrentAngle, sweepAngle);
}
mCurrentAngle = sweepAngle;
canvas.drawPath(mPath, mPaint);
}
At intervals, I'm updating mElapsedTime and calling invalidate(). However, nothing is drawn on the screen. I've tried several variations, but to no avail. Is there something I'm doing wrong? Is there an easier way to do this? Given a percentage of a circle, I want to be able to make that much of the circle be what is drawn on the screen.
There are two things here:
You have to call canvas.drawOval(...) before drawing the arc onto the oval. Otherwise it won't show up. This is why my method didn't work.
Canvas has a drawArc method that takes a start angle and a degrees to sweep out. See Canvas.drawArc(RectF, float, float, boolean, Paint). This is what I was looking for to draw circles.
EDIT: here's the relevant code from my View subclass:
private final Paint mArcPaint = new Paint() {
{
setDither(true);
setStyle(Paint.Style.STROKE);
setStrokeCap(Paint.Cap.ROUND);
setStrokeJoin(Paint.Join.ROUND);
setColor(Color.BLUE);
setStrokeWidth(40.0f);
setAntiAlias(true);
}
};
private final Paint mOvalPaint = new Paint() {
{
setStyle(Paint.Style.FILL);
setColor(Color.TRANSPARENT);
}
};
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF mOval = new RectF(left, top, right, bottom); //This is the area you want to draw on
float sweepAngle = 270; //Calculate how much of an angle you want to sweep out here
canvas.drawOval(mOval, mOvalPaint);
canvas.drawArc(mOval, 269, sweepAngle, false, mArcPaint); //270 is vertical. I found that starting the arc from just slightly less than vertical makes it look better when the circle is almost complete.
}
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)