public class SLCircleProgress extends View {
private static final int ARC_FULL_DEGREE = 360;
private static final int COUNT = 200;
private static final float ARC_EACH_PROGRESS = ARC_FULL_DEGREE * 1.0f / (COUNT - 1);
private int ARC_LINE_LENGTH;
private int ARC_LINE_WIDTH;
private int width, height;
private float max, progress;
private Paint progressPaint;
private Paint textPaint;
private Paint textBgPaint;
private int circleRadius;
private int centerX, centerY;
public SLCircleProgress(Context context) {
super(context);
init();
}
public SLCircleProgress(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SLCircleProgress(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
progressPaint = new Paint();
progressPaint.setAntiAlias(true);
textPaint = new Paint();
textPaint.setColor(Color.TRANSPARENT);
textPaint.setAntiAlias(true);
textBgPaint = new Paint();
textBgPaint.setAntiAlias(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (width == 0 || height == 0) {
width = MiscUtil.measure(widthMeasureSpec, MiscUtil.dipToPx(getContext(), Constant.DEFAULT_SIZE));
height = MiscUtil.measure(heightMeasureSpec, MiscUtil.dipToPx(getContext(), Constant.DEFAULT_SIZE));
circleRadius = Math.min(width, height) / 2;
ARC_LINE_LENGTH = 10;
ARC_LINE_WIDTH = 2;
centerX = width / 2;
centerY = height / 2;
}
Log.e("shiroKenn", "width + height --- " + width + " --- " + height);
Log.e("shiroKenn", "widthMeasureSpec + heightMeasureSpec --- " + widthMeasureSpec + " --- " + heightMeasureSpec);
}
private Rect textBounds = new Rect();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float start = (360 - ARC_FULL_DEGREE) >> 1;
float sweep1 = ARC_FULL_DEGREE * (progress / max);
progressPaint.setColor(Color.WHITE);
progressPaint.setStrokeWidth(ARC_LINE_WIDTH);
float drawDegree = 1.6f;
while (drawDegree <= ARC_FULL_DEGREE) {
double a = (start + drawDegree) / 180 * Math.PI;
float lineStartX = centerX - circleRadius * (float) Math.sin(a);
float lineStartY = centerY + circleRadius * (float) Math.cos(a);
float lineStopX = lineStartX + ARC_LINE_LENGTH * (float) Math.sin(a);
float lineStopY = lineStartY - ARC_LINE_LENGTH * (float) Math.cos(a);
if (drawDegree > sweep1) {
progressPaint.setColor(Color.GRAY);
progressPaint.setStrokeWidth(ARC_LINE_WIDTH >> 1);
}
canvas.drawLine(lineStartX, lineStartY, lineStopX, lineStopY, progressPaint);
drawDegree += ARC_EACH_PROGRESS;
}
textBgPaint.setStyle(Paint.Style.FILL);
textBgPaint.setColor(Color.TRANSPARENT);
canvas.drawCircle(centerX, centerY, (circleRadius - ARC_LINE_LENGTH) * 0.8f, textBgPaint);
textBgPaint.setStyle(Paint.Style.STROKE);
textBgPaint.setStrokeWidth(2);
textBgPaint.setColor(Color.TRANSPARENT);
canvas.drawCircle(centerX, centerY, (circleRadius - ARC_LINE_LENGTH) * 0.8f, textBgPaint);
textPaint.setTextSize(circleRadius >> 1);
String text = (int) (100 * progress / max) + "";
float textLen = textPaint.measureText(text);
textPaint.getTextBounds("8", 0, 1, textBounds);
float h1 = textBounds.height();
canvas.drawText(text, centerX - textLen / 2, centerY - circleRadius / 10 + h1 / 2, textPaint);
textPaint.setTextSize(circleRadius >> 3);
textPaint.getTextBounds("分", 0, 1, textBounds);
float h11 = textBounds.height();
canvas.drawText("分", centerX + textLen / 2 + 5, centerY - circleRadius / 10 + h1 / 2 - (h1 - h11), textPaint);
textPaint.setTextSize(circleRadius / 6);
text = "点击优化";
textLen = textPaint.measureText(text);
canvas.drawText(text, centerX - textLen / 2, centerY + circleRadius / 2.5f, textPaint);
}
public void setMax(int max) {
this.max = max;
invalidate();
}
public void setProgress(final float progress) {
new Thread(new Runnable() {
@Override
public void run() {
float oldProgress = SLCircleProgress.this.progress;
for (int i = 1; i <= 100; i++) {
SLCircleProgress.this.progress = oldProgress + (progress - oldProgress) * (1.0f * i / 100);
postInvalidate();
SystemClock.sleep(20);
}
}
}).start();
}
public void setProgressSync(float progress) {
this.progress = progress;
invalidate();
}
public String calColor(float fraction, String startValue, String endValue) {
int start_a, start_r, start_g, start_b;
int end_a, end_r, end_g, end_b;
start_a = getIntValue(startValue, 1, 3);
start_r = getIntValue(startValue, 3, 5);
start_g = getIntValue(startValue, 5, 7);
start_b = getIntValue(startValue, 7, 9);
end_a = getIntValue(endValue, 1, 3);
end_r = getIntValue(endValue, 3, 5);
end_g = getIntValue(endValue, 5, 7);
end_b = getIntValue(endValue, 7, 9);
return "#" + getHexString((int) (start_a + fraction * (end_a - start_a)))
+ getHexString((int) (start_r + fraction * (end_r - start_r)))
+ getHexString((int) (start_g + fraction * (end_g - start_g)))
+ getHexString((int) (start_b + fraction * (end_b - start_b)));
}
private int getIntValue(String hexValue, int start, int end) {
return Integer.parseInt(hexValue.substring(start, end), 16);
}
private String getHexString(int value) {
String a = Integer.toHexString(value);
if (a.length() == 1) {
a = "0" + a;
}
return a;
}
private boolean isDragging = false;
@Override
public boolean onTouchEvent(@NonNull MotionEvent event) {
float currentX = event.getX();
float currentY = event.getY();
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
if (checkOnArc(currentX, currentY)) {
float newProgress = calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE * max;
setProgressSync(newProgress);
if (listener != null) {
listener.onProgressChanged(calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE);
}
isDragging = true;
}
break;
case MotionEvent.ACTION_MOVE:
if (isDragging) {
if (checkOnArc(currentX, currentY)) {
setProgressSync(calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE * max);
if (listener != null) {
listener.onProgressChanged(calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE);
}
} else {
isDragging = false;
}
}
break;
case MotionEvent.ACTION_UP:
isDragging = false;
break;
}
return true;
}
private boolean checkOnArc(float currentX, float currentY) {
float distance = calDistance(currentX, currentY, centerX, centerY);
float degree = calDegreeByPosition(currentX, currentY);
return distance + 20 > circleRadius - ARC_LINE_WIDTH * 5 && distance - 20 < circleRadius + ARC_LINE_WIDTH * 5
&& (degree >= -20 && degree <= ARC_FULL_DEGREE + 20);
}
private float calDegreeByPosition(float currentX, float currentY) {
float a1 = (float) (Math.atan(1.0f * (centerX - currentX) / (currentY - centerY)) / Math.PI * 180);
if (currentY < centerY) {
a1 += 180;
} else if (currentY > centerY && currentX > centerX) {
a1 += 360;
}
return a1 - (360 - ARC_FULL_DEGREE) / 2;
}
private float calDistance(float x1, float y1, float x2, float y2) {
return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
public interface OnProgressChangeListener {
void onProgressChanged(float progress);
}
private OnProgressChangeListener listener;
public void setOnProgressChangeListener(OnProgressChangeListener onProgressChangeListener) {
this.listener = onProgressChangeListener;
}
}
public class MiscUtil {
public static int measure(int measureSpec, int defaultSize) {
int result = defaultSize;
int specMode = View.MeasureSpec.getMode(measureSpec);
int specSize = View.MeasureSpec.getSize(measureSpec);
if (specMode == View.MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == View.MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
return result;
}
public static int dipToPx(Context context, float dip) {
float density = context.getResources().getDisplayMetrics().density;
return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
}
public static String getPrecisionFormat(int precision) {
return "%." + precision + "f";
}
public static <T> T[] reverse(T[] arrays) {
if (arrays == null) {
return null;
}
int length = arrays.length;
for (int i = 0; i < length / 2; i++) {
T t = arrays[i];
arrays[i] = arrays[length - i - 1];
arrays[length - i - 1] = t;
}
return arrays;
}
public static float measureTextHeight(Paint paint) {
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
return (Math.abs(fontMetrics.ascent) - fontMetrics.descent);
}
}