Android 实现圆形进度条(仪表盘)(二)
public class CircleBarView extends View {
private Paint outPaint, innerPaint;
private Paint mTextPaint, mValueTextPaint, mUnitTextPaint;
private final RectF oval = new RectF();
//最大进度
private int max = 100;
//当前进度
private int progress = 0;
//文本内容
private String text = "";
//数值内容
private String valueText = "";
//单位内容
private String unitText = "";
// 圆弧颜色
private int roundColor = ResourceUtil.getColor(R.color.color_f25);
// 设置数值颜色
private int valueColor = ResourceUtil.getColor(R.color.color_f25);
//用于动画
private float nowPro = 0;
private ValueAnimator animator;
// 文字间距
private int textMargin;
// 圆弧宽度、半径和数值、小间距
private int roundWidth, radius, smallMargin;
// view的高度
private int height;
// 中心点
private final Point centerPoint = new Point();
// 测量文字的范围
private final Rect textRect = new Rect();
public CircleBarView(Context context) {
this(context, null);
}
public CircleBarView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleBarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context);
}
public int getMax() {
return max;
}
public void setMax(int max) {
this.max = max;
}
public int getProgress() {
return progress;
}
public void setProgress(int progress) {
if (this.progress == progress) {
return;
}
if (animator != null && animator.isRunning()) {
animator.cancel();
}
animator = ValueAnimator.ofFloat(nowPro, progress);
animator.setDuration(1000);
animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(animation -> {
nowPro = (float) animation.getAnimatedValue();
CircleBarView.this.progress = (int) nowPro;
valueText = String.valueOf(CircleBarView.this.progress);
invalidate();
});
animator.start();
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getValueText() {
return valueText;
}
public void setValueText(String valueText) {
this.valueText = valueText;
}
public String getUnitText() {
return unitText;
}
public void setUnitText(String unitText) {
this.unitText = unitText;
}
public void setRoundColor(int roundColor) {
this.roundColor = roundColor;
invalidate();
}
public void setValueColor(int valueColor) {
this.valueColor = valueColor;
invalidate();
}
private void initAttrs(Context context) {
// 文字间距
textMargin = dp2px(context, 5);
// 圆弧宽度
roundWidth = dp2px(context, 5);
// 半径
radius = dp2px(context, 72);
height = radius;
// 小间距
smallMargin = roundWidth / 2;
outPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
outPaint.setColor(ResourceUtil.getColor(R.color.color_f6));
outPaint.setStyle(Paint.Style.STROKE);
outPaint.setStrokeCap(Paint.Cap.ROUND);
outPaint.setStrokeWidth(roundWidth);
innerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
innerPaint.setColor(ResourceUtil.getColor(R.color.color_f15));
innerPaint.setStyle(Paint.Style.STROKE);
innerPaint.setStrokeCap(Paint.Cap.ROUND);
innerPaint.setStrokeWidth(roundWidth);
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setColor(ResourceUtil.getColor(R.color.color_ff3));
mTextPaint.setTextSize(sp2px(getContext(), 12));
mValueTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mValueTextPaint.setColor(valueColor);
mValueTextPaint.setTextSize(sp2px(getContext(), 24));
mUnitTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mUnitTextPaint.setColor(ResourceUtil.getColor(R.color.color_ff3));
mUnitTextPaint.setTextSize(sp2px(getContext(), 12));
//动画
animator = ValueAnimator.ofFloat(0, progress);
animator.setDuration(1000);
animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(animation -> {
nowPro = (float) animation.getAnimatedValue();
postInvalidate();
});
animator.start();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpecSize, (int) height);
} else if (widthSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpecSize, heightSpecSize);
height = heightSpecSize;
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpecSize, (int) height);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 中心点
centerPoint.set(getWidth() / 2, height / 2);
// 计算圆弧显示的范围,height * 2 是因为圆弧的高度是根据园来算的,所以双倍才是半个圆
oval.set(getWidth() / 2f - radius, (float) roundWidth, getWidth() / 2f + radius, height * 2 - roundWidth*2);
}
@Override
protected void onDraw(Canvas canvas) {
// 最外层圆弧
canvas.drawArc(oval, 180, 180, false, outPaint); //绘制外层圆弧
// 进度圆弧
if (nowPro > 0) {
innerPaint.setColor(roundColor);
} else {
innerPaint.setColor(ResourceUtil.getColor(R.color.color_f6));
}
canvas.drawArc(oval, 180, 180f * nowPro / max, false, innerPaint); //绘制圆弧
// 文字从底部从下往上绘制。
int valueY = height - smallMargin;
// 值
float valueTextWidth = mValueTextPaint.measureText(valueText);
float unitTextWidth = mUnitTextPaint.measureText(unitText);
canvas.drawText(valueText, centerPoint.x - (valueTextWidth + unitTextWidth) / 2f, valueY, mValueTextPaint);
// 单位
canvas.drawText(unitText, centerPoint.x + valueTextWidth / 2f - unitTextWidth / 2f + smallMargin, valueY, mUnitTextPaint);
// 测量值的高度
mValueTextPaint.getTextBounds(text, 0, text.length(), textRect);
// 提示文字("高血压")
float textWidth = mTextPaint.measureText(text);
// 文字的高度 = 值的高度起点 - 值的高度 - 间距
float textY = valueY - textRect.height() - textMargin;
canvas.drawText(text, centerPoint.x - textWidth / 2f, textY, mTextPaint);
}
/**
* dp 转 px
*/
private int dp2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* sp 转 px
*/
private int sp2px(Context context, float spValue) {
final float scale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * scale + 0.5f);
}
}
使用方法:
xml中
<xxx.xxx.CircleBarView
android:id="@+id/circle_bar_view"
android:layout_width="match_parent"
android:layout_height="80dp"/>
activity中 :
circleBarView.setText("高风险");
circleBarView.setValueText("0");
circleBarView.setUnitText("分");
circleBarView.setProgress(100);