项目中需要用到环形控制条,查找了资料然后,修改了各位网友的成果,最后达到我们项目中的要求.先看看效果.
主要参考文章:http://blog.csdn.net/alijiahua/article/details/51474580 在此感谢alijiahua的博客
核心代码只有两行:canvas.drawArc() canvas.drawText(),其中要用到三角函数,计算起始位置和角度,至今我还是不太 懂,不过我知道写完这篇文章理理就差不多了.
过程是这样的
第一集成View
第二在value下写attr属性文件
第三画底层灰色的圆弧
第四根据手势再绘制顶层的进度
第五画白色的手柄
第六画文字
在value文件夹下属性文件如下
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ColorCircleProgressView"> <!--定义圆环的渐变颜色 这里只用到了color1和color2--> <attr name="Color01" format="color"/> <attr name="Color02" format="color"/> <attr name="Color03" format="color"/> <attr name="Color04" format="color"/> <attr name="Color05" format="color"/> <attr name="Color06" format="color"/> <attr name="Color07" format="color"/>
<attr name="ColorBackGround" format="color"/><!--定义圆环背景色,默认灰色-->
<attr name="TextColor" format="color"/> <!--定义圆环角度及开始位置的角度, 不建议修改起始角度和viewAngle 圆环到view边框的距离--> <attr name="ViewAngle" format="integer"/> <attr name="StartAngle" format="integer"/> <attr name="ViewPadding" format="integer"/> <!--定义圆环的大小,是否圆角--> <attr name="StrokeWith" format="integer"/> <attr name="IsRound" format="boolean"/> <!--定义温度区间 默认16-30度--> <attr name="StartTemperature" format="integer"/> <attr name="EndTemperature" format="integer"/> <attr name="ProgressTemperature" format="integer"/> <!--定义点的大小和颜色--> <attr name="PointColor" format="color"/> <attr name="PointRadio" format="integer"/> </declare-styleable></resources><!--定义文字背景色,默认灰色-->
自定义的View
这里允许我再BB两点package com.example.heaterbaby.circleview1; import android.animation.ArgbEvaluator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.SweepGradient; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import com.example.heaterbaby.R; /** * Created by 瑜哥 on 2017/6/17. */ public class ColorCircleProgressView extends View { private Paint mPaint,shader_paint; private int mStrokeWith; private boolean mIsRound; private int mColor01= 0xff4455ff, mColor02= 0xff987456, mColor03= 0xffFF4081, mColor04= 0xff123456, mColor05= 0xffFF9981, mColor06= 0xff99ffff, mColor07= 0xff66ff33, backGroundColor= Color.GRAY, textColor = Color.GRAY; private int mViewAangle; private int mStartAangle; private int mViewPadding; private Paint paint_dot; private int mPointColor; private int mPointRaido; private float mView_x0; private float mView_y0; private int mPointAngle=45; private OnProgressListener mOnProgressListener; private ArgbEvaluator argbEvaluator;//颜色渐变插值器 private int startTemperature; private int endTemperature; private int progressTemperature; private SweepGradient sweepGradient; private final String TEMP_TEXT = "℃"; public void setOnProgressListener(OnProgressListener onProgressListener) { mOnProgressListener = onProgressListener; } public ColorCircleProgressView(Context context) { super(context); } public ColorCircleProgressView(Context context, AttributeSet attrs) { super(context, attrs); argbEvaluator = new ArgbEvaluator();//颜色渐变插值器 /*获取属性集合*/ TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ColorCircleProgressView, 0, 0); /*渐变颜色值*/ mColor01 = typedArray.getColor(R.styleable.ColorCircleProgressView_Color01, mColor01); mColor02 = typedArray.getColor(R.styleable.ColorCircleProgressView_Color02, mColor02); mColor03 = typedArray.getColor(R.styleable.ColorCircleProgressView_Color03, mColor03); mColor04 = typedArray.getColor(R.styleable.ColorCircleProgressView_Color04, mColor04); mColor05 = typedArray.getColor(R.styleable.ColorCircleProgressView_Color05, mColor05); mColor06 = typedArray.getColor(R.styleable.ColorCircleProgressView_Color06, mColor06); mColor07 = typedArray.getColor(R.styleable.ColorCircleProgressView_Color07, mColor07); backGroundColor = typedArray.getColor(R.styleable.ColorCircleProgressView_Color07, backGroundColor); textColor = typedArray.getColor(R.styleable.ColorCircleProgressView_TextColor, textColor); /*圆环角度,开始的角度,到边框的距离*/ mViewAangle = typedArray.getInteger(R.styleable.ColorCircleProgressView_ViewAngle, 270); mStartAangle = typedArray.getInteger(R.styleable.ColorCircleProgressView_StartAngle, 135); mViewPadding = typedArray.getInteger(R.styleable.ColorCircleProgressView_ViewPadding, 50); /*圆环的大小及是否圆角*/ mStrokeWith = typedArray.getInteger(R.styleable.ColorCircleProgressView_StrokeWith, 20); mIsRound = typedArray.getBoolean(R.styleable.ColorCircleProgressView_IsRound, true); /*手柄Point的颜色和大小*/ mPointColor = typedArray.getColor(R.styleable.ColorCircleProgressView_PointColor, Color.WHITE); mPointRaido = typedArray.getInteger(R.styleable.ColorCircleProgressView_PointRadio, 30); /**获取圆环起始温度和当前设置温度*/ startTemperature = typedArray.getInteger(R.styleable.ColorCircleProgressView_StartTemperature, 16); endTemperature = typedArray.getInteger(R.styleable.ColorCircleProgressView_EndTemperature, 30); progressTemperature = typedArray.getInteger(R.styleable.ColorCircleProgressView_ProgressTemperature, 26); convertTempToDegree(progressTemperature); /*设置圆环画笔*/ SetPaint(); } /** * 初始化画笔 */ private void SetPaint() { paint_dot = new Paint();//手柄画笔 paint_dot.setColor(mPointColor); mPaint = new Paint();//背景灰色画笔 mPaint.setStyle(Paint.Style.STROKE); /*画笔为线条线条*/ mPaint.setStrokeWidth(mStrokeWith); /*线条的宽*/ mPaint.setAntiAlias(true); shader_paint = new Paint();//进度画笔 shader_paint.setStyle(Paint.Style.STROKE); /*画笔为线条线条*/ shader_paint.setStrokeWidth(mStrokeWith); /*线条的宽*/ shader_paint.setAntiAlias(true); /*抗锯齿*/ shader_paint.setStrokeCap(Paint.Cap.ROUND); if(mIsRound) {mPaint.setStrokeCap(Paint.Cap.ROUND);} /*是否圆角*/ } @Override protected void onDraw(Canvas canvas) { /*得到view的宽高*/ int width = getWidth(); int height = getHeight(); /*把宽高赋值给全局变量,得到圆心的坐标*/ mView_x0=width/2; mView_y0=height/2; /*设置线性渐变*/ sweepGradient = new SweepGradient(width/ 2, height/ 2, new int[]{ mColor01, mColor02}, null); mPaint.setColor(backGroundColor); /*温度文本*/ String temp=""; /*定义圆环的所占的矩形区域:注意view一定为正方形*/ RectF rectF = new RectF(0 + mViewPadding, 0 + mViewPadding, width - mViewPadding, width - mViewPadding); /*根据矩形区域画扇形:因为sweep的起点在右边中心处,所以先旋转90度画布*/ canvas.rotate(90,width/2,height/2); canvas.drawArc(rectF, mStartAangle - 90, mViewAangle, false, mPaint);//画底层灰色 /*动态获取圆上起始点的坐标*/ //圆点坐标:width/2,height/2 //半径:(width-mViewPadding-mViewPadding)/2 //角度:a0 if(mPointAngle<=45){mPointAngle=45;} else if(mPointAngle>315&mPointAngle<=360){mPointAngle=315;} /*将45-315范围的角度转为0-100*/ if(mOnProgressListener!=null) { // int progress = (int)((mPointAngle - 45) / 2.7); int progress = (int)((mPointAngle - 45) / (mViewAangle/(endTemperature-startTemperature)));//(270/(36-16))=mViewAangle/(endTemperature-startTemperature) // mOnProgressListener.onScrollingListener(startTemperature+progress);//温度的回调 mOnProgressListener.onScrollingListener(mPointAngle); temp=String.valueOf(startTemperature + progress); } //颜色差值器 Integer color = (Integer) argbEvaluator.evaluate((mPointAngle - 45) / 270f, mColor01, mColor02); float x0=width/2; float y0=height/2; float R = (float) ((width - mViewPadding - mViewPadding) / 2); float Point_x= (float) (x0+R*Math.cos(mPointAngle*3.14/180)); float Point_y= (float) (y0+R*Math.sin(mPointAngle * 3.14 / 180)); shader_paint.setShader(sweepGradient); if (mPointAngle !=45) {//如果为45度,也就是起始位置时会画整个弧形 canvas.drawArc(rectF, mStartAangle - 90, mPointAngle-45, false, shader_paint);//画划过的弧度 } paint_dot.setStrokeWidth(mStrokeWith/4); canvas.drawCircle(Point_x,Point_y,mPointRaido, paint_dot);//画点 手柄 Paint paint_nano = new Paint(); paint_nano.setStrokeWidth(mStrokeWith/8); paint_nano.setColor(color); canvas.drawCircle(Point_x,Point_y,mPointRaido/2, paint_nano);//画点 手柄内原点颜色 canvas.save();//保存以上内容,否则写文字会旋转90度 canvas.rotate(-90,width/2,height/2);//旋转画布-90度 paint_nano.setColor(textColor); paint_nano.setTextSize(width*0.2f); Rect rect = new Rect(); paint_nano.getTextBounds(temp,0,temp.length(),rect);//测量温度文本大小 canvas.drawText(temp,width/2-rect.width()/2,height/2+rect.height()/2,paint_nano);//绘制温度 paint_nano.setTextSize(width*0.1f);//使画笔变小 canvas.drawText(TEMP_TEXT,width/2+rect.width()/2,height/2-rect.height()/2,paint_nano);//绘制℃ //计算底部起始温度,结束温度的位置 完全是实验出来的数据 float pointX_startT= (float) (x0+R*Math.cos(45+90*3.14/180)); float pointY_startT= (float) (y0+R*Math.sin(45 +90* 3.14 / 180)); float pointX_endT= (float) (x0+R*Math.cos(45*3.14/180)); float pointY_endT= (float) (y0+R*Math.sin(45 * 3.14 / 180)); paint_nano.setTextSize(width*0.08f); Rect rect1 = new Rect(); paint_nano.getTextBounds(TEMP_TEXT,0,TEMP_TEXT.length(),rect1);//测量起始温度文本大小 canvas.drawText(startTemperature+"",pointX_startT+rect1.width()/2, (float) (pointY_startT+rect1.height()*3.5),paint_nano);//绘制起始温度 canvas.drawText(endTemperature+"",pointX_endT-rect1.width(),pointY_endT+rect1.height()*2,paint_nano);//绘制结束温度 //保存以上内容 canvas.restore(); } @Override public boolean onTouchEvent(MotionEvent event) { /*获取点击位置的坐标*/ float Action_x = event.getX(); float Action_y = event.getY(); /*根据坐标转换成对应的角度*/ float get_x0 = Action_x - mView_x0; float get_y0 = Action_y - mView_y0; /*01:左下角区域*/ if(get_x0<=0&get_y0>=0){ float tan_x = get_x0 * (-1); float tan_y = get_y0; double atan = Math.atan(tan_x / tan_y); mPointAngle= (int) Math.toDegrees(atan); } /*02:左上角区域*/ if(get_x0<=0&get_y0<=0){ float tan_x = get_x0 * (-1); float tan_y = get_y0*(-1); double atan = Math.atan(tan_y / tan_x); mPointAngle= (int) Math.toDegrees(atan)+90; } /*03:右上角区域*/ if(get_x0>=0&get_y0<=0){ float tan_x = get_x0 ; float tan_y = get_y0*(-1); double atan = Math.atan(tan_x/ tan_y); mPointAngle= (int) Math.toDegrees(atan)+180; } /*04:右下角区域*/ if(get_x0>=0&get_y0>=0){ float tan_x = get_x0 ; float tan_y = get_y0; double atan = Math.atan(tan_y / tan_x); mPointAngle= (int) Math.toDegrees(atan)+270; } /*得到点的角度后进行重绘*/ invalidate(); return true; } //温度回调的接口 public interface OnProgressListener{ public void onScrollingListener(Integer progress); } /** * 设置当前温度 * @param progerss 应大于等于 startTemporary 小于等于 endTemperature */ private void convertTempToDegree(int progerss) { if (progerss <= endTemperature && progerss >= startTemperature) { int i = progerss - startTemperature; int total = endTemperature - startTemperature; float rate = (float)270 / (float)total; mPointAngle = (int) (i * rate)+45;//加上起始角度 } } /** * 设置温度 * @param temp */ public void setTemperature(int temp) { convertTempToDegree(temp); invalidate(); } }
1:onDraw里面的逻辑 :弧形的起始角度为45-315,然后将弧形顺时针旋转90度 (canvas.rotate),画背景灰色的弧度(canvas.drawArc),然后初始化时将温度转换为角度并画上去(canvas.drawArc),这里需要用到SweepGradient 产生颜色渐变的弧度,并把这个设置给画笔,
接下来就要开始画手柄那个点 白色的实心点canvas.drawCircle 然后在用argbEvaluator差值器计算出当前弧度所需要的颜色,并设置给画笔,之后在缩小画笔的尺寸,在画刚才计算出颜色的点.最后在画问题,需要注意的是需要计算下文本的位置paint.getTextBounds
这里有个坑是之前旋转了90度的画布,所以画文本是旋转90度 的,那需要把当前的内容先保存 canvas.save();//保存以上内容,否则写文字会旋转90度 canvas.rotate(-90,width/2,height/2);//旋转画布-90度回到初始时
2最核心的onTouchEvent 三角函数的计算
这里分别把按下去的点分为4个象限去分别处理,我已第三象限为例子,还特意自己纯手工画了图
/*获取点击位置的坐标*/ float down_x = event.getX(); float down_y = event.getY(); /*获取三角形两条直角边*/ float x_height = down_x - x0; float y_height = down_y - y0; /*01:左下角区域*/ if(x_height<=0&y_height>=0){ float tan_x = x_height * (-1); float tan_y = y_height; double atan = Math.atan(x_height / y_height); mPointAngle= (int) Math.toDegrees(atan);//得到相对水平方向的角度 }