public class Pm2.5CircleView extend View {
private int mWidth;
private int mHeight;
private int diameter = 500; //直径
private float centerX; //圆心X坐标
private float centerY; //圆心Y坐标
private Paint allArcPaint;
private Paint progressPaint;
private Paint vTextPaint;
private Paint hintPaint;
private Paint degreePaint;
private Paint curSpeedPaint;
private RectF bgRect;
private ValueAnimator progressAnimator;
private PaintFlagsDrawFilter mDrawFilter;
private SweepGradient sweepGradient;
private Matrix rotateMatrix;
private float startAngle = 105;
private float sweepAngle = 270;
private float currentAngle = 330; //105 - 330
private float lastAngle;
private int[] colors = new int[]{Color.GREEN, Color.YELLOW, Color.RED, Color.RED};
private float maxValues = 60;
private float curValues = 100;
private float bgArcWidth = dipToPx(2);
private float progressWidth = dipToPx(10);
private float textSize = dipToPx(60);
private float hintSize = dipToPx(15);
private float curSpeedSize = dipToPx(13);
private int aniSpeed = 1000;
private float longdegree = dipToPx(13);
private float shortdegree = dipToPx(5);
private final int DEGREE_PROGRESS_DISTANCE = dipToPx(8);
private String hintColor = "#676767";
private String longDegreeColor = "#111111";
private String shortDegreeColor = "#111111";
private String bgArcColor = "#111111";
private String titleString;
private String hintString;
private boolean isShowCurrentSpeed = true;
private boolean isNeedTitle;
private boolean isNeedUnit;
private boolean isNeedDial;
private boolean isNeedContent;
private Drawable mThumb, mThumbPress;
private int paddingOuterThumb;
// sweepAngle / maxValues 的值
private float k;
private Paint vTextPaint_zhi;
private RectF oval;
private float radius;//圆环半径
/**
* 当前进度
*/
private int progress = 190;//最大对应498-》150刻度
//progress = 310(实际值) - 0刻度
//progress =375(实际值) - 50(对应虚拟刻度(pm2.5))
//progress =439(实际值) - 100(对应虚拟刻度(pm2.5))
//progress =498(实际值) - 150(对应虚拟刻度(pm2.5))
//progress =61 (实际值)- 200(对应虚拟刻度(pm2.5))
//progress =124(实际值) - 250(对应虚拟刻度(pm2.5))
//progress =190(实际值) - 500(对应虚拟刻度(pm2.5))
public ColorArcProgressBar(Context context) {
super(context, null);
initView();
}
public ColorArcProgressBar(Context context, AttributeSet attrs) {
super(context, attrs, 0);
initCofig(context, attrs);
initView();
}
public ColorArcProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initCofig(context, attrs);
initView();
}
/**
* 初始化布局配置
* @param context
* @param attrs
*/
private void initCofig(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ColorArcProgressBar);
int color1 = a.getColor(R.styleable.ColorArcProgressBar_front_color1, Color.GREEN);
int color2 = a.getColor(R.styleable.ColorArcProgressBar_front_color2, color1);
int color3 = a.getColor(R.styleable.ColorArcProgressBar_front_color3, color1);
int color4 = a.getColor(R.styleable.ColorArcProgressBar_front_color4, color1);
int color5 = a.getColor(R.styleable.ColorArcProgressBar_front_color5, color1);
colors = new int[]{color1, color2, color3,color4, color5,color1};
sweepAngle = a.getInteger(R.styleable.ColorArcProgressBar_total_engle, 270);
bgArcWidth = a.getDimension(R.styleable.ColorArcProgressBar_back_width, dipToPx(2));
progressWidth = a.getDimension(R.styleable.ColorArcProgressBar_front_width, dipToPx(10));
isNeedTitle = a.getBoolean(R.styleable.ColorArcProgressBar_is_need_title, false);
isNeedContent = a.getBoolean(R.styleable.ColorArcProgressBar_is_need_content, false);
isNeedUnit = a.getBoolean(R.styleable.ColorArcProgressBar_is_need_unit, false);
isNeedDial = a.getBoolean(R.styleable.ColorArcProgressBar_is_need_dial, false);
hintString = a.getString(R.styleable.ColorArcProgressBar_string_unit);
titleString = a.getString(R.styleable.ColorArcProgressBar_string_title);
curValues = a.getFloat(R.styleable.ColorArcProgressBar_current_value, 100);
maxValues = a.getFloat(R.styleable.ColorArcProgressBar_max_value, 60);
// setCurrentValues(curValues);
setMaxValues(maxValues);
a.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = (int) (2 * longdegree + progressWidth + diameter + 2 * DEGREE_PROGRESS_DISTANCE);
int height= (int) (2 * longdegree + progressWidth + diameter + 2 * DEGREE_PROGRESS_DISTANCE);
setMeasuredDimension(width, height);
}
private void initView() {
diameter = 3 * getScreenWidth() / 5;
//弧形的矩阵区域
bgRect = new RectF();
bgRect.top = longdegree + progressWidth/2 + DEGREE_PROGRESS_DISTANCE;
bgRect.left = longdegree + progressWidth/2 + DEGREE_PROGRESS_DISTANCE;
bgRect.right = diameter + (longdegree + progressWidth/2 + DEGREE_PROGRESS_DISTANCE);
bgRect.bottom = diameter + (longdegree + progressWidth/2 + DEGREE_PROGRESS_DISTANCE);
oval = new RectF(longdegree + progressWidth/2 + DEGREE_PROGRESS_DISTANCE + progressWidth/2
, longdegree + progressWidth/2 + DEGREE_PROGRESS_DISTANCE + progressWidth/2,
diameter + (longdegree + progressWidth/2 + DEGREE_PROGRESS_DISTANCE) -progressWidth/2,
diameter + (longdegree + progressWidth/2 + DEGREE_PROGRESS_DISTANCE) - progressWidth/2);//目的让字体显示出来
//圆心
centerX = (2 * longdegree + progressWidth + diameter + 2 * DEGREE_PROGRESS_DISTANCE)/2;
centerY = (2 * longdegree + progressWidth + diameter + 2 * DEGREE_PROGRESS_DISTANCE)/2;
//圆环半径
radius = centerX - (longdegree + progressWidth/2 + DEGREE_PROGRESS_DISTANCE + progressWidth/2);
//外部刻度线
degreePaint = new Paint();
degreePaint.setColor(Color.parseColor(longDegreeColor));
//整个弧形
allArcPaint = new Paint();
allArcPaint.setAntiAlias(true);
allArcPaint.setStyle(Paint.Style.STROKE);
allArcPaint.setStrokeWidth(bgArcWidth);
allArcPaint.setColor(Color.parseColor(bgArcColor));
allArcPaint.setStrokeCap(Paint.Cap.ROUND);
//当前进度的弧形
progressPaint = new Paint();
progressPaint.setAntiAlias(true);
progressPaint.setStyle(Paint.Style.STROKE);
progressPaint.setStrokeCap(Paint.Cap.ROUND);
progressPaint.setStrokeWidth(progressWidth);
progressPaint.setColor(Color.GREEN);
//内容显示文字
vTextPaint = new Paint();
vTextPaint.setTextSize(textSize);
vTextPaint.setColor(Color.BLACK);
vTextPaint.setTextAlign(Paint.Align.CENTER);
//指针数字显示
vTextPaint_zhi = new Paint();
vTextPaint_zhi.setTextSize(dipToPx(15));
vTextPaint_zhi.setColor(Color.BLACK);
vTextPaint_zhi.setTextAlign(Paint.Align.CENTER);
//显示单位文字
hintPaint = new Paint();
hintPaint.setTextSize(hintSize);
hintPaint.setColor(Color.parseColor(hintColor));
hintPaint.setTextAlign(Paint.Align.CENTER);
//显示标题文字
curSpeedPaint = new Paint();
curSpeedPaint.setTextSize(curSpeedSize);
curSpeedPaint.setColor(Color.parseColor(hintColor));
curSpeedPaint.setTextAlign(Paint.Align.CENTER);
mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
sweepGradient = new SweepGradient(centerX, centerY, colors, null);
rotateMatrix = new Matrix();
//加载进度小圆点
mThumbPress = getResources().getDrawable(R.drawable.btn_lamp_plate_down);// 圆点图片
int thumbHalfheight = mThumbPress.getIntrinsicHeight() / 2;
int thumbHalfWidth = mThumbPress.getIntrinsicWidth() / 2;
mThumbPress.setBounds(-thumbHalfWidth, -thumbHalfheight, thumbHalfWidth, thumbHalfheight);
paddingOuterThumb = thumbHalfheight;//按下的白色图标
minValidateTouchArcRadius = (int) (radius - paddingOuterThumb*1.5f);
maxValidateTouchArcRadius = (int) (radius + paddingOuterThumb*1.5f);
}
@Override
protected void onDraw(Canvas canvas) {
//抗锯齿
canvas.setDrawFilter(mDrawFilter);
if (isNeedDial) {
//画刻度线
for (int i = 0; i < 40; i++) {
if (i > 15 && i < 25) {
canvas.rotate(9, centerX, centerY);
continue;
}
if (i % 5 == 0) {//0,5,10,15,25,35,
degreePaint.setStrokeWidth(dipToPx(2));
switch (i) {
case 0: //45
degreePaint.setColor(Color.parseColor("#FE1303"));//红
canvas.drawText("150",
centerX,centerY - diameter / 2 - progressWidth - DEGREE_PROGRESS_DISTANCE + progressWidth/2 , vTextPaint_zhi);
break;
case 5://90
degreePaint.setColor(Color.parseColor("#AB033D"));//红褐色
canvas.drawText("200",
centerX,centerY - diameter / 2 - progressWidth - DEGREE_PROGRESS_DISTANCE + progressWidth/2 , vTextPaint_zhi);
break;
case 10://135
degreePaint.setColor(Color.parseColor("#450F6F"));//浅褐色
canvas.drawText("300",
centerX,centerY - diameter / 2 - progressWidth - DEGREE_PROGRESS_DISTANCE + progressWidth/2 , vTextPaint_zhi);
break;
case 15://180
degreePaint.setColor(Color.parseColor("#450F6C"));//深褐色
canvas.drawText("500",
centerX,centerY - diameter / 2 - progressWidth - DEGREE_PROGRESS_DISTANCE + progressWidth/2 , vTextPaint_zhi);
break;
case 25://225
degreePaint.setColor(Color.parseColor("#8BCD1E"));//绿
canvas.drawText("0",
centerX,centerY - diameter / 2 - progressWidth - DEGREE_PROGRESS_DISTANCE + progressWidth/2 , vTextPaint_zhi);
break;
case 30://270
degreePaint.setColor(Color.parseColor("#E3DF1A"));//前黄绿
canvas.drawText("50",
centerX,centerY - diameter / 2 - progressWidth - DEGREE_PROGRESS_DISTANCE + progressWidth/2 , vTextPaint_zhi);
break;
case 35://315
degreePaint.setColor(Color.parseColor("#ED870D"));//黄红
canvas.drawText("100",
centerX,centerY - diameter / 2 - progressWidth - DEGREE_PROGRESS_DISTANCE + progressWidth/2 , vTextPaint_zhi);
break;
}
canvas.drawCircle(centerX,centerY - diameter / 2 - progressWidth / 2 - DEGREE_PROGRESS_DISTANCE +progressWidth/2
,dipToPx(3),degreePaint);
//float cx, float cy, float radius, @NonNull Paint paint
}
canvas.rotate(9, centerX, centerY);//每隔,
}
}
//整个弧
canvas.drawArc(oval, startAngle, sweepAngle, false, allArcPaint);
//设置渐变色
rotateMatrix.setRotate(130, centerX, centerY);
sweepGradient.setLocalMatrix(rotateMatrix);
progressPaint.setShader(sweepGradient);
//当前进度
canvas.drawArc(oval, startAngle, currentAngle, false, progressPaint);
if (isNeedContent) {
canvas.drawText(String.format("%.0f", curValues), centerX, centerY + textSize / 3, vTextPaint);
}
if (isNeedUnit) {
canvas.drawText(hintString, centerX, centerY + 2 * textSize / 3, hintPaint);
}
if (isNeedTitle) {
canvas.drawText(titleString, centerX, centerY - 2 * textSize / 3, curSpeedPaint);
}
//画移动白点
//paint.setStyle(Paint.Style.FILL_AND_STROKE);
PointF progressPoint = ChartUtil.calcArcEndPointXY(centerX, centerY, radius, 360 * progress / maxValues, 270);
//canvas.drawCircle(progressPoint.x, progressPoint.y, pointRadius, paint);
// 画Thumb
canvas.save();
canvas.translate(progressPoint.x, progressPoint.y);
mThumbPress.draw(canvas);
canvas.restore();
invalidate();
}
public static class ChartUtil {
/**
* 依圆心坐标,半径,扇形角度,计算出扇形终射线与圆弧交叉点的xy坐标
* @param cirX
* @param cirY
* @param radius
* @param cirAngle
* @return
*/
public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, float cirAngle){//一个在270度那里,一个从270度开始叠加。
// //centerX, centerY, radius, 0, 270
//cirAngle = (orginAngle + cirAngle) % 360;
float posX = 0.0f;
float posY = 0.0f;
//将角度转换为弧度
float arcAngle = (float) (Math.PI * cirAngle / 180.0);
if (cirAngle < 90)
{
posX = cirX + (float)(Math.cos(arcAngle)) * radius;
posY = cirY + (float)(Math.sin(arcAngle)) * radius;
}
else if (cirAngle == 90)
{
posX = cirX;
posY = cirY + radius;
}
else if (cirAngle > 90 && cirAngle < 180)
{
arcAngle = (float) (Math.PI * (180 - cirAngle) / 180.0);
posX = cirX - (float)(Math.cos(arcAngle)) * radius;
posY = cirY + (float)(Math.sin(arcAngle)) * radius;
}
else if (cirAngle == 180)
{
posX = cirX - radius;
posY = cirY;
}
else if (cirAngle > 180 && cirAngle < 270)
{
arcAngle = (float) (Math.PI * (cirAngle - 180) / 180.0);
posX = cirX - (float)(Math.cos(arcAngle)) * radius;
posY = cirY - (float)(Math.sin(arcAngle)) * radius;
}
else if (cirAngle == 270)
{
posX = cirX;
posY = cirY - radius;
}
else
{
arcAngle = (float) (Math.PI * (360 - cirAngle) / 180.0);
posX = cirX + (float)(Math.cos(arcAngle)) * radius;
posY = cirY - (float)(Math.sin(arcAngle)) * radius;
}
return new PointF(posX, posY); //new PointF();// 运动小点的位置坐标
}
public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, float cirAngle, float orginAngle){
//centerX, centerY, radius, 0, 270
cirAngle = (orginAngle + cirAngle) % 360;
return calcArcEndPointXY(cirX, cirY, radius, cirAngle);
}
}
private boolean downOnArc = false;
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
if (isTouchArc(x, y)) {
downOnArc = true;
updateArc(x, y);
return true;
}
break;
case MotionEvent.ACTION_MOVE:
if (downOnArc) {
updateArc(x, y);
return true;
}
break;
case MotionEvent.ACTION_UP:
downOnArc = false;
invalidate();
// if (changeListener != null) {
// changeListener.onProgressChangeEnd(max, progress);
// }
break;
}
return super.onTouchEvent(event);
}
// 根据点的位置,更新进度
private void updateArc(int x, int y) {
int cx = x - getWidth() / 2;
int cy = y - getHeight() / 2;
// 计算角度,得出(-1->1)之间的数据,等同于(-180°->180°)
double angle = Math.atan2(cy, cx)/Math.PI;
// 将角度转换成(0->2)之间的值,然后加上90°的偏移量
angle = ((2 + angle)%2 + (90/180f))%2;
// 用(0->2)之间的角度值乘以总进度,等于当前进度
progress = (int) (angle * maxValues/2);
// if (changeListener != null) {
// changeListener.onProgressChange(max, progress);
// }
Log.e("robin debug","progress:" + progress);
invalidate();
}
/**
* 设置进度,此为线程安全控件,由于考虑多线的问题,需要同步
* 刷新界面调用postInvalidate()能在非UI线程刷新
* @param progress
*/
public synchronized void setProgress(int progress) {
if(progress < 0){
throw new IllegalArgumentException("progress not less than 0");
}
if(progress > 498){//maxProgress
progress = (int) 498;
}
if(progress <= 498){
this.progress = progress;
postInvalidate();
}
}
private int minValidateTouchArcRadius; // 最小有效点击半径
private int maxValidateTouchArcRadius; // 最大有效点击半径
// 判断是否按在圆边上
private boolean isTouchArc(int x, int y) {
double d = getTouchRadius(x, y);
if (d >= minValidateTouchArcRadius && d <= maxValidateTouchArcRadius) {
return true;
}
return false;
}
// 计算某点到圆点的距离
private double getTouchRadius(int x, int y) {
int cx = x - getWidth() / 2;
int cy = y - getHeight() / 2;
return Math.hypot(cx, cy);
}
/**
* 设置最大值
* @param maxValues
*/
public void setMaxValues(float maxValues) {
this.maxValues = maxValues;
k = sweepAngle/maxValues;
}
/**
* 设置当前值
* @param currentValues
*/
public void setCurrentValues(float currentValues) {
if (currentValues > maxValues) {
currentValues = maxValues;
}
if (currentValues < 0) {
currentValues = 0;
}
this.curValues = currentValues;
lastAngle = currentAngle;
setAnimation(lastAngle, currentValues * k, aniSpeed);
}
/**
* 设置整个圆弧宽度
* @param bgArcWidth
*/
public void setBgArcWidth(int bgArcWidth) {
this.bgArcWidth = bgArcWidth;
}
/**
* 设置进度宽度
* @param progressWidth
*/
public void setProgressWidth(int progressWidth) {
this.progressWidth = progressWidth;
}
/**
* 设置速度文字大小
* @param textSize
*/
public void setTextSize(int textSize) {
this.textSize = textSize;
}
/**
* 设置单位文字大小
* @param hintSize
*/
public void setHintSize(int hintSize) {
this.hintSize = hintSize;
}
/**
* 设置单位文字
* @param hintString
*/
public void setUnit(String hintString) {
this.hintString = hintString;
invalidate();
}
/**
* 设置直径大小
* @param diameter
*/
public void setDiameter(int diameter) {
this.diameter = dipToPx(diameter);
}
/**
* 设置标题
* @param title
*/
private void setTitle(String title){
this.titleString = title;
}
/**
* 设置是否显示标题
* @param isNeedTitle
*/
private void setIsNeedTitle(boolean isNeedTitle) {
this.isNeedTitle = isNeedTitle;
}
/**
* 设置是否显示单位文字
* @param isNeedUnit
*/
private void setIsNeedUnit(boolean isNeedUnit) {
this.isNeedUnit = isNeedUnit;
}
/**
* 设置是否显示外部刻度盘
* @param isNeedDial
*/
private void setIsNeedDial(boolean isNeedDial) {
this.isNeedDial = isNeedDial;
}
/**
* 为进度设置动画
* @param last
* @param current
*/
private void setAnimation(float last, float current, int length) {
progressAnimator = ValueAnimator.ofFloat(last, current);
progressAnimator.setDuration(length);
progressAnimator.setTarget(currentAngle);
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentAngle= (float) animation.getAnimatedValue();
curValues = currentAngle/k;
}
});
progressAnimator.start();
}
/**
* dip 转换成px
* @param dip
* @return
*/
private int dipToPx(float dip) {
float density = getContext().getResources().getDisplayMetrics().density;
return (int)(dip * density + 0.5f * (dip >= 0 ? 1 : -1));
}
/**
* 得到屏幕宽度
* @return
*/
private int getScreenWidth() {
WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.widthPixels;
}