项目中需要用到环形控制条,查找了资料然后,修改了各位网友的成果,最后达到我们项目中的要求.先看看效果.
主要参考文章: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
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();
}
}
这里允许我再BB两点
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);//得到相对水平方向的角度 }