环形手势控制条

来源:互联网 发布:数据的一致性是指 编辑:程序博客网 时间:2024/05/22 16:38

项目中需要用到环形控制条,查找了资料然后,修改了各位网友的成果,最后达到我们项目中的要求.先看看效果.

主要参考文章: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);//得到相对水平方向的角度        }