How to draw a circle with animation in android with circle size based on a value
You have to draw the circle view, and after that you should create an animation to it.
Creating the circle view:
public class Circle extends View { private static final int START_ANGLE_POINT = 90; private final Paint paint; private final RectF rect; private float angle; public Circle(Context context, AttributeSet attrs) { super(context, attrs); final int strokeWidth = 40; paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(strokeWidth); //Circle color paint.setColor(Color.RED); //size 200x200 example rect = new RectF(strokeWidth, strokeWidth, 200 + strokeWidth, 200 + strokeWidth); //Initial Angle (optional, it can be zero) angle = 120; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawArc(rect, START_ANGLE_POINT, angle, false, paint); } public float getAngle() { return angle; } public void setAngle(float angle) { this.angle = angle; }}
Creating the animation class to set the new angle:
public class CircleAngleAnimation extends Animation { private Circle circle; private float oldAngle; private float newAngle; public CircleAngleAnimation(Circle circle, int newAngle) { this.oldAngle = circle.getAngle(); this.newAngle = newAngle; this.circle = circle; } @Override protected void applyTransformation(float interpolatedTime, Transformation transformation) { float angle = oldAngle + ((newAngle - oldAngle) * interpolatedTime); circle.setAngle(angle); circle.requestLayout(); }}
Put circle into your layout:
<com.package.Circle android:id="@+id/circle" android:layout_width="300dp" android:layout_height="300dp" />
And finally starting the animation:
Circle circle = (Circle) findViewById(R.id.circle);CircleAngleAnimation animation = new CircleAngleAnimation(circle, 240);animation.setDuration(1000);circle.startAnimation(animation);
The result is:
As extra from @JohnCordeiro answer.I have added parameters from xml to reuse the circle and to fill the circle if needed.
class RecordingCircle(context: Context, attrs: AttributeSet) : View(context, attrs) {private val paint: Paintprivate val rect: RectFprivate val fillPaint: Paintprivate val fillRect: RectFvar angle: Floatvar startAngle: Floatinit { val typedArray = context.obtainStyledAttributes(attrs, R.styleable.RecordingCircle) startAngle = typedArray.getFloat(R.styleable.RecordingCircle_startAngle, 0f) val offsetAngle = typedArray.getFloat(R.styleable.RecordingCircle_offsetAngle, 0f) val color = typedArray.getColor(R.styleable.RecordingCircle_color, ResourcesCompat.getColor(resources, R.color.recording, null)) val strokeWidth = typedArray.getFloat(R.styleable.RecordingCircle_strokeWidth, 20f) val circleSize = typedArray.getDimension(R.styleable.RecordingCircle_cicleSize, 100f) val fillColor = typedArray.getColor(R.styleable.RecordingCircle_fillColor, 0) typedArray.recycle() paint = Paint().apply { setAntiAlias(true) setStyle(Paint.Style.STROKE) setStrokeWidth(strokeWidth) setColor(color) } rect = RectF( strokeWidth, strokeWidth, (circleSize - strokeWidth), (circleSize - strokeWidth) ) fillPaint = Paint().apply { setAntiAlias(true) setStyle(Paint.Style.FILL) setColor(fillColor) } val offsetFill = strokeWidth fillRect = RectF( offsetFill, offsetFill, (circleSize - offsetFill), (circleSize - offsetFill) ) //Initial Angle (optional, it can be zero) angle = offsetAngle}override protected fun onDraw(canvas: Canvas) { super.onDraw(canvas) if (fillColor > 0) { canvas.drawArc(rect, 0f, 360f, false, fillPaint) } canvas.drawArc(rect, startAngle, angle, false, paint)}}
And on the xml:
<com.myapp.RecordingCircle android:id="@+id/cameraRecordButton" android:layout_width="match_parent" android:layout_height="match_parent" app:offsetAngle="360" app:color="@color/light_grey" app:strokeWidth="10" app:cicleSize="@dimen/camera_record_button" app:fillColor="@color/recording_bg" /> <com.myapp.RecordingCircle android:id="@+id/progress" android:layout_width="match_parent" android:layout_height="match_parent" app:startAngle="270" app:color="@color/recording" app:strokeWidth="10" app:cicleSize="@dimen/camera_record_button" />
Here the result: Note the semi-transparent fill of the button
Added code for calculating correct circle measurements
import android.content.Contextimport android.graphics.Canvasimport android.graphics.Colorimport android.graphics.Paintimport android.graphics.RectFimport android.util.AttributeSetimport android.view.Viewimport androidx.core.content.ContextCompatclass Circle(context: Context, attrs: AttributeSet) : View(context, attrs) { private val paint: Paint private val rect: RectF var angle = 0f companion object { private val START_ANGLE_POINT = 270f } init { val strokeWidth = resources.getDimension(R.dimen.toast_circle_stroke_width) paint = Paint().apply { setAntiAlias(true) setStyle(Paint.Style.STROKE) setStrokeWidth(strokeWidth) setColor(Color.RED) } val circleSize = resources.getDimension(R.dimen.toast_circle_size) rect = RectF( strokeWidth, strokeWidth, circleSize + strokeWidth, circleSize + strokeWidth ) } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val circleSize = resources.getDimension(R.dimen.toast_circle_size).toInt() val strokeWidth = resources.getDimension(R.dimen.toast_circle_stroke_width).toInt() super.onMeasure( MeasureSpec.makeMeasureSpec(circleSize + 2 * strokeWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(circleSize + 2 * strokeWidth, MeasureSpec.EXACTLY)); } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawArc(rect, START_ANGLE_POINT, angle, false, paint) }}