Android 自定义类似仪表盘样式的半圆环带刻度可拖动SeekBar进度条效果

一、背景

最近准备要做一个类似仪表盘样式的半圆环带刻度可拖动的进度条来展示和设置温度,网上找了找demo,都和实际的需求有点区别,感觉这种功能实现起来不难,就索性自己弄了一个。

二、正文

在开始之前,我们来看下实际的运行效果,看看能不能满足正在查找资料的小伙伴的项目需求:

这里写图片描述

效果就是这样的啦,没什么特别的地方,我们来看下实现方法吧,如果对于自定义View的流程还有些不清楚,可以查看一下自定义简单View及获取xml自定义属性,里面总结了自定义View的整体思路和流程,在本例中我们可以将要实现的View分解成一个个基本的小功能,再集中优势力量各个击破,思路如下:

1.继承一个View或者一个我们需要的View的子类,并添加构造方法
2.重写onMeasure方法
3.重写onDraw方法 ,在这里我们将上面的View分解为灰色的半圆环、蓝色的半圆环、环上的按钮图标、灰色刻度线、蓝色刻度线、中间显示的进度几个部分
4.重写onTouchEvent方法,获取用户触屏状态,再进行相应处理
5.在xml中自定义View属性

下面来让我们跟着上面的思路来看看具体的实现代码吧,这是我们的自定义View类,我们添加了不同参数的构造方法,如果你只需要在xml中使用当前的自定义View,我们只需要添加两个参数的构造方法即可

public class MyHalfCricularSlideWithScaleView extends View{

    //在代码中直接new的时候,会调用一个参数构造方法
    public MyHalfCricularSlideWithScaleView(Context context) {
        this(context, null);
    }

    //在xml中使用自定义控件的时候,不管有没有使用我们自己的自定义属性都会调用两个参数构造方法
    public MyHalfCricularSlideWithScaleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    //这个构造方法系统不会默认调用,需要我们自己主动调用
    public MyHalfCricularSlideWithScaleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

    }
}

这是我们所声明的变量,看着数量这么多特别吓人,但其实我告诉你这并不是一蹴而就的,而是你在编写的过程中,陆陆续续发现少了参数而不断添加后的结果,而且由于我是先写了demo后才写的博客,这两者并不是同步的,所以就造成onDraw方法里实现绘制每个小功能的初始化代码都放在了一起,正常的写的话都是按照上面步骤一点一点实现的。

    //圆环的宽度
    float ringWidth;
    //底部的圆弧颜色
    int ringBgCorlor;
    //滑动的圆弧颜色
    int slideRingCorlor;
    //同心圆的外圆半径
    int radius;
    //中间字的颜色
    int wordCorlor;
    //中间字的大小
    int wordSize;

    //最大进度范围
    int maxProgress;
    //最小进度范围
    int minProgress;
    //当前进度(总是将起始位置等分为100份),通过进度的百分比求出实际显示数值
    int progress;
    //实际显示的数值
    double realShowProgress;
    //每次要增加减少的数值
    double addOrReduce = 1;
    //开始滑动的起始位置度数,顶部270 右侧0 底部90 左侧180,因为这是半圆直接写死从左侧180开始滑动
    int beginLocation;
    //当前可滑动区域的范围
    int slideAbleLocation;

    //圆环上的圆圈
    Bitmap mDragBitmap;
    //圆环的宽
    int bitmapWidth;
    //圆环的高度
    int bitmapHight;

    //外侧刻度线的数量
    int scaleLineCount;
    //外侧正常刻度线的长度
    int scaleLineLength;
    //线条的宽度
    int scaleLineWidth;
    //需要特殊处理的刻度线长度,例如正方位上的刻度或者当前刻度
    int specialScaleLineLength;
    //刻度结束的角度
    float sweepAngle = 180;//因为是半圆,这里就写死了
    //未选择的刻度线颜色
    int scaleLineNormalCorlor;
    //滑动后的刻度线颜色
    int specialScaleCorlor;
    //刻度线距离里面的环的距离
    int scaleToRingSpace;

    //画底部背景环的画笔
    Paint ringBgPaint;
    //画上面圆弧的画笔
    Paint slideRingPaint;
    //圆环上的小圆圈
    Paint mBitmapPaint;
    //写当前progress的画笔
    Paint wordPaint;
    //画普通背景刻度线的画笔
    Paint scalePaint;
    //这是画滑动后的刻度颜色画笔
    Paint specialScalePaint;

    //显示中间显示文字(当前为progress)所占的区域
    Rect rect;

    //这是保留小数的使用类
    DecimalFormat df;

正是由于我是写了demo之后才写的博客,所以我就直接先展示我们所需要的view属性了,你如果想自己从头实现一个,完全可以给所需要的变量一个写死的测试值,到功能实现之后,再将测试值替换成xml属性。言归正传,我们在values目录下新建attrs.xml,并创建declare-styleable,如下:

<!--自定义的半圆环带刻度可拖动SeekBar进度条效果-->
    <declare-styleable name="MyHalfCircularSildeView"
        >
        <!--圆环的宽度-->
        <attr name="ringWidth" format="float"/>
        <!--滑动圆弧的背景-->
        <attr name="slideRingCorlor" format="integer"/>
        <!--底部圆弧的背景-->
        <attr name="ringBgCorlor" format="integer"/>
        <!--当前文字的背景-->
        <attr name="wordCorlor" format="integer"/>
        <!--当前文字的大小-->
        <attr name="wordSize" format="integer"/>
        <!--同心圆外圆的半径-->
        <attr name="radius" format="integer"/>
        <!--最大显示进度-->
        <attr name="maxProgress" format="integer"/>
        <!--最小显示进度-->
        <attr name="minProgress" format="integer"/>
        <!--当前显示进度-->
        <attr name="progress" format="integer"/>

        <!--下面这是刻度线的一些属性-->
        <!--刻度线的总数-->
        <attr name="scaleLineCount" format="integer"/>
        <!--普通刻度线的长度-->
        <attr name="scaleLineLength" format="integer"/>
        <!--特殊刻度线的长度-->
        <attr name="specialScaleLineLength" format="integer"/>
        <!--线条的宽度-->
        <attr name="scaleLineWidth" format="integer"/>
        <!--刻度线距离圆环的宽度-->
        <attr name="scaleToRingSpace" format="integer"/>
        <!--普通线的颜色-->
        <attr name="scaleLineNormalCorlor" format="integer"/>
        <!--特殊线的颜色-->
        <attr name="specialScaleCorlor" format="integer"/>
    </declare-styleable>

这是我们的变量初始化代码,没有什么可说的

private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        //dip2px这个方法就是将dp转换成px
        slideAbleLocation = CommentUtil.dip2px(context, 30);

        bitmapWidth = CommentUtil.dip2px(context, 30);
        bitmapHight = CommentUtil.dip2px(context, 30);

        //设置圆环上的小圆图标的大小
        mDragBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.ring_dot);
        mDragBitmap = CommentUtil.conversionBitmap(mDragBitmap
                , bitmapWidth
                , bitmapHight);

        //获取xml中定义属性,并对需要的变量进行初始化
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs
                , R.styleable.MyHalfCircularSildeView
                , defStyleAttr, 0);
        ringWidth = array.getInt(R.styleable.MyHalfCircularSildeView_ringWidth, CommentUtil.dip2px(context, 10));
        slideRingCorlor = array.getInt(R.styleable.MyHalfCircularSildeView_slideRingCorlor, 0xFF6a6aff);
        ringBgCorlor = array.getInt(R.styleable.MyHalfCircularSildeView_ringBgCorlor, 0xFFbebebe);
        radius = array.getInt(R.styleable.MyHalfCircularSildeView_radius, CommentUtil.dip2px(context, 100));
        wordCorlor = array.getInt(R.styleable.MyHalfCircularSildeView_wordCorlor, Color.BLUE);
        wordSize = array.getInt(R.styleable.MyHalfCircularSildeView_wordSize, 18);
        maxProgress = array.getInt(R.styleable.MyHalfCircularSildeView_maxProgress, 100);
        minProgress = array.getInt(R.styleable.MyHalfCircularSildeView_minProgress, 0);
        progress = array.getInt(R.styleable.MyHalfCircularSildeView_progress, 15);
        //因为这是个半弧,所以我们直接写死了,从左侧开始
        beginLocation = 180;
        //下面是刻度线的属性
        scaleLineCount = array.getInt(R.styleable.MyHalfCircularSildeView_scaleLineCount, 100);
        scaleLineLength = array.getInt(R.styleable.MyHalfCircularSildeView_scaleLineLength, CommentUtil.dip2px(context, 10));
        specialScaleLineLength = array.getInt(R.styleable.MyHalfCircularSildeView_specialScaleLineLength, CommentUtil.dip2px(context, 15));
        scaleToRingSpace = array.getInt(R.styleable.MyHalfCircularSildeView_scaleToRingSpace, CommentUtil.dip2px(context, 10));
        scaleLineNormalCorlor = array.getInt(R.styleable.MyHalfCircularSildeView_scaleLineNormalCorlor, 0xFFbebebe);
        specialScaleCorlor = array.getInt(R.styleable.MyHalfCircularSildeView_specialScaleCorlor, 0xFF6a6aff);
        scaleLineWidth = array.getInt(R.styleable.MyHalfCircularSildeView_scaleLineWidth, CommentUtil.dip2px(context, 2));
        //记得使用完销毁
        array.recycle();

        //保留1位小数
        df = new DecimalFormat("#.0");
        realShowProgress = getShowProgress(progress);
    }

这是我们在初始化圆环上小图标mDragBitmap位图的时候,设置宽高的工具方法

    //转换bitmap宽高
    public static Bitmap conversionBitmap(Bitmap bitmap, int newWidth, int newHeight) {
        Bitmap b = bitmap;
        int width = b.getWidth();
        int height = b.getHeight();
        // 计算缩放比例
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        // 取得想要缩放的matrix参数
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);
        // 得到新的图片
        return Bitmap.createBitmap(b, 0, 0, width, height, matrix, true);
    }

这是我们要根据刻度数量获取展示数字的方法,因为目前要求刻度线的数量为100,但是进度范围不一定是0-100,所以这就要求我们要根据当前的刻度数转化成实际要显示的数字,例如10-90范围内,滑动了50个刻度,当前刻度为(90-10) * (50.0/100) + 10 = 50

扫描二维码关注公众号,回复: 3226062 查看本文章
    /** 根据progress,再求出如果首位不是0-100的时候的数字*/
    private double getShowProgress(int progress) {
        return Double.parseDouble(df.format((maxProgress - minProgress)/100.0 * progress + minProgress));
    }

这是我们初始画笔的方法

private void initPaint(Context context) {
        //画背景圆弧的画笔初始化
        ringBgPaint = new Paint();
        ringBgPaint.setColor(ringBgCorlor);
        ringBgPaint.setAntiAlias(true);// 抗锯齿效果
        ringBgPaint.setStyle(Paint.Style.STROKE);//设置空心
        ringBgPaint.setStrokeWidth(ringWidth);//线宽度,即环宽
        ringBgPaint.setStrokeCap(Paint.Cap.ROUND);//圆形笔头

        //画滑动圆弧的画笔初始化
        slideRingPaint = new Paint();
        slideRingPaint.setAntiAlias(true);
        slideRingPaint.setStyle(Paint.Style.STROKE);
        slideRingPaint.setColor(slideRingCorlor);
        slideRingPaint.setStrokeWidth(ringWidth);
        slideRingPaint.setStrokeCap(Paint.Cap.ROUND);//圆形笔头

        //写中间文字的画笔初始化
        wordPaint = new Paint();
        wordPaint.setColor(wordCorlor);
        wordPaint.setTextSize(CommentUtil.sp2px(context, wordSize));
        rect = new Rect();
        String str = progress+" C";
        wordPaint.getTextBounds(str, 0, str.length(),rect);

        //设置圆环上圆圈的画笔初始化
        mBitmapPaint = new Paint();
        mBitmapPaint.setDither(true);//设置防抖动
        mBitmapPaint.setFilterBitmap(true);//对Bitmap进行滤波处理
        mBitmapPaint.setAntiAlias(true);//设置抗锯齿

        //这是画外面刻度线的画笔
        scalePaint = new Paint();
        scalePaint.setColor(scaleLineNormalCorlor);
        scalePaint.setAntiAlias(true);
        scalePaint.setStyle(Paint.Style.STROKE);
        scalePaint.setStrokeWidth(scaleLineWidth);

        //这是画滑动后的刻度线的画笔
        specialScalePaint = new Paint();
        specialScalePaint.setColor(specialScaleCorlor);
        specialScalePaint.setAntiAlias(true);
        specialScalePaint.setStyle(Paint.Style.STROKE);
        specialScalePaint.setStrokeWidth(scaleLineWidth);
    }

重写onMeasure方法,当layout_width和layout_height为wrap_content时,我们要给View设置大小,否则宽高会占据全屏

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthModel = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightModel = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        switch (widthModel) {
            case MeasureSpec.UNSPECIFIED:
            case MeasureSpec.AT_MOST:
                //当时自适应控件的时候,宽度为圆直径+左右边距+2*(刻度线宽+刻度线距离圆环)
                widthSize = 2*radius + getPaddingLeft() + getPaddingRight() +2*(specialScaleLineLength + scaleToRingSpace);
                break;
            case MeasureSpec.EXACTLY:
                break;
        }
        switch (heightModel) {
            case MeasureSpec.UNSPECIFIED:
            case MeasureSpec.AT_MOST:
                //当控件自适应时候,尺寸=半径+上下边距+刻度线宽+刻度线距离圆环+圆环上图标*0.5
                heightSize = radius + getPaddingTop() + getPaddingBottom() + bitmapHight/2 + specialScaleLineLength + scaleToRingSpace;
                break;
            //当宽度全屏或者固定尺寸时候
            case MeasureSpec.EXACTLY:
                break;
        }
        setMeasuredDimension(widthSize, heightSize);
    }

重写onDraw方法,这是我们画圆环背景的方法,drawArc方法的第一个参数为绘制圆弧所在的矩形区域范围(取左上和右下坐标)、第二个参数设置圆弧是从哪个角度来顺时针绘画的(0:右侧,90下面,180左侧,270上面)、第三个参数设置圆弧扫过的角度、第四个参数设置我们的圆弧在绘画的时候是否经过圆形、第五个参数设置我们的画笔对象的属性。

        //画背景圆环
        canvas.drawArc(new RectF(ringWidth/2 + getPaddingLeft() + specialScaleLineLength + scaleToRingSpace
                        , ringWidth/2 + getPaddingTop() + specialScaleLineLength + scaleToRingSpace
                        , 2*radius- ringWidth/2 + getPaddingLeft() + specialScaleLineLength + scaleToRingSpace
                        , 2*radius - ringWidth/2 + getPaddingTop() + specialScaleLineLength + scaleToRingSpace)
                , beginLocation, 180, false, ringBgPaint);

这是我们画能拖动的圆环的方法

        //画滑动圆弧
        canvas.drawArc(new RectF(ringWidth/2 + getPaddingLeft() + specialScaleLineLength + scaleToRingSpace
                        , ringWidth/2 + getPaddingTop() + specialScaleLineLength + scaleToRingSpace
                        , 2*radius- ringWidth/2 + getPaddingLeft() + specialScaleLineLength + scaleToRingSpace
                        , 2*radius - ringWidth/2 + getPaddingTop() + specialScaleLineLength + scaleToRingSpace)
                , beginLocation, progress * 180/100, false, slideRingPaint);

这是我们画圆环上的小图标的方法,难点主要是求小圆标在圆环不同位置上的圆点坐标,反正我是把数学知识都交给体育老师了,直接在网上找的现成的方法

        //画上滑动图标
        PointF progressPoint = CommentUtil.calcArcEndPointXY(radius + getPaddingLeft() + specialScaleLineLength + scaleToRingSpace
                , radius + getPaddingTop() + specialScaleLineLength + scaleToRingSpace
                , radius - ringWidth/2
                , progress * 180/100, 180);
        int left = (int) progressPoint.x - mDragBitmap.getWidth() / 2;
        int top = (int) progressPoint.y - mDragBitmap.getHeight() / 2;
        canvas.drawBitmap(mDragBitmap, left, top, mBitmapPaint);

这是根据圆心坐标,半径,扇形角度来求扇形终射线与圆弧交叉点的xy坐标的工具方法,哈哈有兴趣的可以深入研究一下

    //依圆心坐标,半径,扇形角度,计算出扇形终射线与圆弧交叉点的xy坐标
    public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, float
            cirAngle, float orginAngle) {
        cirAngle = (orginAngle + cirAngle) % 360;
        return calcArcEndPointXY(cirX, cirY, radius, cirAngle);
    }

    //依圆心坐标,半径,扇形角度,计算出扇形终射线与圆弧交叉点的xy坐标
    public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, float
            cirAngle) {
        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);
    }

这是我们画文字的方法,其中进度可以为整数或者精确到0.1的小数

        //展示进度
        String str = (int)Math.floor(realShowProgress)+" C";//整数
//        String str = realShowProgress + " C";//小数
        wordPaint.getTextBounds(str, 0, str.length(),rect);
        canvas.drawText(str, radius+getPaddingLeft() + specialScaleLineLength + scaleToRingSpace-rect.width()/2
                , radius + getPaddingTop() + specialScaleLineLength + scaleToRingSpace, wordPaint);

这是我们画刻度线的方法,主要就是循环画线,难点在于怎么根据角度获取线段的首位坐标,数学不好又没有找到方法…所以只能换另一种思路,通过不断旋转坐标系的方法,始终让要画的线段都在旋转后的坐标系的y轴之上

    /** 画背景刻度*/
    private void paintScale(Canvas canvas) {
        canvas.save();
        //将坐标系移到圆中心
        canvas.translate(radius + getPaddingLeft() + specialScaleLineLength + scaleToRingSpace
                ,radius + getPaddingTop() + specialScaleLineLength + scaleToRingSpace);
        //旋转坐标系
        canvas.rotate(90);
        for(int i=0; i< scaleLineCount +1; i++){
            //刻度线的实际展示长度
            int scaleLine = scaleLineLength;
            //正方位的线比较长
            if(i == 0 || i == scaleLineCount/2 || i== scaleLineCount){
                scaleLine = specialScaleLineLength;
            }
            //画已经滑动过的刻度线,因为实际刻度数量都是按着100个来转换的
            if(i * (100/scaleLineCount) <= progress){
                canvas.drawLine(0,radius + scaleToRingSpace
                        ,0,radius + scaleToRingSpace + scaleLine
                        ,specialScalePaint);
            }
            //画未滑动到的刻度线
            else{
                canvas.drawLine(0,radius + scaleToRingSpace
                        ,0,radius + scaleToRingSpace + scaleLine
                        ,scalePaint);
            }
            canvas.rotate(sweepAngle/(scaleLineCount * 1f));
        }
        //操作完成后恢复状态
        canvas.restore();
    }

这是获取我们触屏手势状态的方法,主要难点是根据当前坐标获取角度再获取与之相对应的进度的方法,在我们允许的区域内,当手指按下,手指滑动,手指弹起时,不断重绘进度,给人一种圆环被拖着走的错觉,其实这只是被重绘的结果

    @Override
    public synchronized 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 (isOnRing(x, y) && y <= radius + getPaddingTop()+ specialScaleLineLength + scaleToRingSpace) {
                    updateProgress(x, y);
                    return true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if(y <= radius + getPaddingTop() + specialScaleLineLength + scaleToRingSpace){
                    updateProgress(x, y);
                }
                return true;
            case MotionEvent.ACTION_UP:
                invalidate();
                break;
        }
        return super.onTouchEvent(event);
    }

    /** 根据当前点的位置求角度,再转换成当前进度*/
    private void updateProgress(int eventX, int eventY) {
        double angle = Math.atan2(eventY - (radius + getPaddingLeft() + specialScaleLineLength + scaleToRingSpace)
                , eventX - (radius + getPaddingLeft() + specialScaleLineLength + scaleToRingSpace))/Math.PI;
        angle = ((2 + angle) % 2 + (-beginLocation / 180f)) % 2;
        if((int)Math.round(angle * 100) >= 0){
            progress = (int)Math.round(angle * 100);
            realShowProgress = getShowProgress(progress);
        }
        invalidate();
    }

这是我们判断特定坐标是否在滑动区域之内的方法,因为并不是用户在任意都能拖动小图标改变进度,只有按在了规定的可滑动区域内,才能让用户拖动进度条

     /** 判断当前触摸屏幕的位置是否位于咱们定的可滑动区域内*/
    private boolean isOnRing(float eventX, float eventY) {
        boolean result = false;
        double distance = Math.sqrt(Math.pow(eventX - (radius+getPaddingLeft()+ specialScaleLineLength + scaleToRingSpace), 2)
                + Math.pow(eventY - (radius+getPaddingLeft()+ specialScaleLineLength + scaleToRingSpace), 2));
        if (distance < (2*radius+getPaddingLeft()+getPaddingRight()+2*(specialScaleLineLength + scaleToRingSpace))
                && distance > radius - slideAbleLocation){
            result = true;
        }
        return result;
    }

到这里我们要的功能基本已经实现了,我们再对外提供几个公开的方法

    /** 定位到指定刻度*/
    public void setProgress(double p) {
        if(p <= maxProgress && p >= minProgress){
            realShowProgress = p;
            progress = (int)((realShowProgress - minProgress)*100.0/(maxProgress - minProgress));
        }
        invalidate();
    }

    /** 点击增加progress*/
    public void addProgress() {
        if(realShowProgress < maxProgress){
            synchronized (MyHalfCricularSlideWithScaleView.class) {
                realShowProgress = Double.parseDouble(df.format(realShowProgress + addOrReduce));
                progress = (int)((realShowProgress - minProgress)*100.0/(maxProgress - minProgress));
            }
            invalidate();
        }
    }

    /** 点击减少progress*/
    public void reduceProgress() {
        if(realShowProgress > minProgress){
            synchronized (MyHalfCricularSlideWithScaleView.class) {
                realShowProgress = Double.parseDouble(df.format(realShowProgress - addOrReduce));
                progress = (int)((realShowProgress - minProgress)*100.0/(maxProgress - minProgress));
            }
            invalidate();
        }
    }

这是我们的Activity类

public class HomepageActivity extends AppCompatActivity {

    //带刻度的滑动圆环
    MyHalfCricularSlideWithScaleView myHalfCricularSlideWithScaleView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_homepage);
        init();
        addProgressListener();
    }

    private void init() {
        myHalfCricularSlideWithScaleView = (MyHalfCricularSlideWithScaleView)findViewById(R.id.half_circular_scale_view);
    }

    private void addProgressListener() {
        findViewById(R.id.tv_add_scale_progress).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myHalfCricularSlideWithScaleView.addProgress();
            }
        });
        findViewById(R.id.tv_reduce_scale_progress).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myHalfCricularSlideWithScaleView.reduceProgress();
            }
        });
        //设置开始的位置
        myHalfCricularSlideWithScaleView.setProgress(50.0);
    }
}

这是我们的布局文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    >

    <seekbar.custom.mycircularseekbar.widget.MyHalfCricularSlideWithScaleView
        android:id="@+id/half_circular_scale_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        custom:maxProgress="85"
        custom:minProgress="15"
        android:padding="10dp"
        />

    <TextView
        android:id="@+id/tv_add_scale_progress"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:text="增加"
        android:layout_below="@id/half_circular_scale_view"
        android:textSize="16sp"
        android:padding="20dp"
        android:textColor="#FFbebebe"
        android:layout_marginLeft="70dp"
        android:layout_marginRight="20dp"
        android:gravity="center"
        />

    <TextView
        android:id="@+id/tv_reduce_scale_progress"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:text="减少"
        android:layout_below="@id/half_circular_scale_view"
        android:textSize="16sp"
        android:textColor="#FFbebebe"
        android:layout_toRightOf="@id/tv_add_scale_progress"
        android:padding="20dp"
        android:gravity="center"
        />
</RelativeLayout>

三、总结

好了,到这里我们就基本都介绍完了,其实这个自定义View绘制的思路和方法都不难,难的是怎么找到正确的坐标,现在知道数学学得好的优势了吧,不过数学不好的小伙伴也不用气馁,毕竟现在网上的资料这么多,遇到问题好好钻研一下,没有克服不了哒,一起,加油。

源码点击下载

猜你喜欢

转载自blog.csdn.net/MingJieZuo/article/details/80032980