自定义View实现之健康指数监控器

马上要离开现单位了,蛋蛋的忧伤。。。以此纪念吧!

需求:健康指数监控控件
分析:
1、三条不同颜色的圆弧
2、最外层圆弧颜色根据进度不同渐变
3、外层圆弧内部边缘文字描述
4、中心部分文字:健康指数
5、中间层圆弧进度显示
解析:
1、画三条圆弧角度为120°~300°

    /**
     * 准备工作。。
     * 
     * @param canvas
     */
    private void drawAllArc(Canvas canvas) {
        float flagWidth = 0.0f;
        flagWidth = (mWidth - strokeOuterWidth);
        initPaint(outerColor, strokeOuterWidth, strokeOuterWidth,
                strokeOuterWidth, flagWidth, flagWidth);

        mColors = new int[] {// 渐变色数组
        0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 0xFFFFFF00,
                0xFFFF0000 };
        // 渐变着色器
        Shader s = new SweepGradient(0, 0, mColors, null);
        mPaint.setShader(s);
        // 外层 第一段
        canvas.drawArc(oval, startAngle, sweetAngle / 3, false, mPaint);
        mPaint.setColor(getContext().getResources().getColor(
                R.color.outerColor2));
        // 外层 第二段
        canvas.drawArc(oval, startAngle + sweetAngle / 3, sweetAngle / 3,
                false, mPaint);
        mPaint.setColor(getContext().getResources().getColor(
                R.color.outerColor3));
        // 外层 第三段
        canvas.drawArc(oval, startAngle + sweetAngle / 3 * 2, sweetAngle / 3,
                false, mPaint);

        flagWidth = flagWidth - strokeMidWidth;

        initPaint(innerColor,
                strokeMidWidth, mOffset + strokeMidWidth + strokeOuterWidth,
                mOffset + strokeMidWidth + strokeOuterWidth,
                flagWidth - mOffset,
                flagWidth - mOffset);
        // 画中间层
        canvas.drawArc(oval, startAngle, sweetAngle, false, mPaint);

        flagWidth = flagWidth - strokeOuterWidth;

        initPaint(innerColor, strokeInnerWidth, mOffset*2 + strokeInnerWidth + strokeMidWidth + strokeOuterWidth,
                mOffset*2 + strokeInnerWidth + strokeMidWidth + strokeOuterWidth,
                flagWidth - mOffset*2, 
                flagWidth - mOffset*2);
        // 画内层
        canvas.drawArc(oval, startAngle, sweetAngle, false, mPaint);
    }

其中initPaint()方法是对画笔的一些初始化操作,如下:

    /**
     * 初始化/重置 画笔
     * 
     * @param outerColor
     * @param strokeOuterWidth
     * @param left
     * @param top
     * @param right
     * @param bottom
     */
    private void initPaint(int outerColor, float strokeOuterWidth, float left,
            float top, float right, float bottom) {
        mPaint.reset();
        mPaint.setColor(outerColor);
        mPaint.setStrokeWidth(strokeOuterWidth);
        mPaint.setAntiAlias(true); // 消除锯齿
        mPaint.setStyle(Paint.Style.STROKE); // 设置空心
        oval = new RectF(left, top, right, bottom);
    }

oval 是它所在的矩形,由此确定位置。
2、颜色渐变

mColors = new int[] {// 渐变色数组
        0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 0xFFFFFF00,
                0xFFFF0000 };
        // 渐变着色器
        Shader s = new SweepGradient(0, 0, mColors, null);
        mPaint.setShader(s);

主要用到 SweepGradient 为 mPaint 设置着色器,第三个参数为颜色数组,以此来确定渐变颜色值。(该方法具体使用请参考官方Api)
3、外层圆弧内部边缘文字描述

/**
     * 主要用到api path.addArc 和 canvas.drawTextOnPath
     * 具体参数可以上网查查,这个应该可以计算出一套算法的,我这里是一点点试出来的,(好尴尬,不会算法)
     * 
     * @param canvas
     */
    private void drawShowTextOnPath(Canvas canvas) {
        RectF ovalF = new RectF(strokeOuterWidth, strokeOuterWidth, mWidth
                - strokeOuterWidth, mWidth - strokeOuterWidth);
        float vOffset = 45;
        mTextPath.reset();
        mTextPath.addArc(ovalF, startAngle, 5);
        canvas.drawTextOnPath("0", mTextPath, 0, vOffset, textPaint);
        // 每次要 reset
        mTextPath.reset();
        mTextPath.addArc(ovalF, startAngle, 150 - 75);
        canvas.drawTextOnPath("差", mTextPath, 0, vOffset, textPaint);

        mTextPath.reset();
        mTextPath.addArc(ovalF, startAngle, 150);
        canvas.drawTextOnPath("25", mTextPath, 0, vOffset, textPaint);

        mTextPath.reset();
        mTextPath.addArc(ovalF, startAngle, 150 + 75);
        canvas.drawTextOnPath("中", mTextPath, 0, vOffset, textPaint);

        mTextPath.reset();
        mTextPath.addArc(ovalF, startAngle, 300);
        canvas.drawTextOnPath("50", mTextPath, 0, vOffset, textPaint);

        mTextPath.reset();
        mTextPath.addArc(ovalF, sweetAngle, 25);
        canvas.drawTextOnPath("良", mTextPath, 0, vOffset, textPaint);

        mTextPath.reset();
        mTextPath.addArc(ovalF,  sweetAngle, 100);
        canvas.drawTextOnPath("75", mTextPath, 0, vOffset, textPaint);

        mTextPath.reset();
        mTextPath.addArc(ovalF, sweetAngle, 165);
        canvas.drawTextOnPath("优", mTextPath, 0, vOffset, textPaint);

        mTextPath.reset();
        mTextPath.addArc(ovalF, sweetAngle , 225);
        canvas.drawTextOnPath("100", mTextPath, 0, vOffset, textPaint);
    }

主要用到api path.addArc 和 canvas.drawTextOnPath,具体使用方法参考官方Api
4、中心部分文字:健康指数

/**
     * 画文字--健康指数质量
     * 
     * @param canvas
     */
    private void setMidText(Canvas canvas) {
        mTextStrPaint.setTextSize(22);
        mTextStrPaint.getTextBounds(strText, 0, strText.length(), boundsText);
        Log.e("VIEW_LOG_TAG", boundsText.right + "," + boundsText.bottom + ","
                + boundsText.top + "," + boundsText.left);
        canvas.drawText(strText, mWidth/2 - (float) (boundsText.right / 2)
                + (float) (boundsText.left / 2), mWidth/2 + mTextOffset, mTextStrPaint);
    }

    /**
     * 画文字--健康指数数量
     * 
     * @param canvas
     */
    private void setMidTextNum(Canvas canvas) {
        mTextNumPaint.setTextSize(45);
        mTextNumPaint.getTextBounds(strTextNum, 0, strTextNum.length(), bounds);
        System.out.println(bounds.right + "," + bounds.bottom);
        Log.e("VIEW_LOG_TAG", bounds.right + "," + bounds.bottom + ","
                + bounds.top + "," + bounds.left);
        canvas.drawText(strTextNum, mWidth/2 - (float) (bounds.right / 2)
                + (float) (bounds.left / 2), mWidth/2, mTextNumPaint);
    }

其中难点就是要计算文字显示位置,首先确定文字所在矩形

mTextStrPaint.getTextBounds(strText, 0, strText.length(), boundsText);

然后调用 drawText

android.graphics.Canvas.drawText(String text, float x, float y, Paint paint)

其中 x , y 是确定它所绘制的位置

x = mWidth/2 - (float) (boundsText.right / 2)
                + (float) (boundsText.left / 2);
y = mWidth/2 + mTextOffset;

由总宽度的一半减去文字的一半就可以确定文字开始绘制的位置了。
5、中间层圆弧进度显示

    /**
     * 根据当前进度画线
     * 
     * @param canvas
     */
    private void setAngleProgress(Canvas canvas) {

        initPaint(midColor, strokeMidWidth, 
                mOffset  + strokeMidWidth + strokeOuterWidth,
                mOffset + strokeMidWidth + strokeOuterWidth,
                mWidth - strokeOuterWidth - strokeMidWidth - mOffset,
                mWidth - strokeOuterWidth - strokeMidWidth - mOffset);
        float progress = this.mProgress * sweetAngle / 100.0f;
        // 画中间层
        canvas.drawArc(oval, startAngle, progress, false, mPaint);
    }

其原理就是在中间弧线上根据进度绘制一条弧线,因为总弧度为300°,所以其转换为当前进度(0-100)* 300 / 100 , 这样就可以计算出需要绘制的进度了。
至此整个过程已经结束了,其中不足之处就是绘制外层圆弧文字的时候计算问题,没有找到规律,对Api使用不是太熟悉,如果有哪位朋友能改善希望告知,不胜感激!
对了,还有适配的问题,在这里我是重写 onMeasure

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        /**
         * 设置宽度
         */
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);
        if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
        {
            mWidth = specSize;
        }
        /***
         * 设置高度
         */

        specMode = MeasureSpec.getMode(heightMeasureSpec);
        specSize = MeasureSpec.getSize(heightMeasureSpec);
        if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
        {
            mHeight = specSize;
        }

        mWidth = mHeight = Math.max(mWidth, mHeight);
        if(mWidth > mScreenWidth || mWidth == 0){
            mWidth = mScreenWidth;
        }
        Log.e("xxx", "EXACTLY" + mWidth + "  " + mHeight);
        setMeasuredDimension(mWidth,mWidth); // 强制长宽相等

    }

如果 xml 里没有指定宽高或者指定的高度大于屏幕宽度的话则
mWidth = mScreenWidth;
然后 setMeasuredDimension(mWidth,mWidth); // 强制长宽相等

代码下载地址demo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值