Android 弧形SeekBar自定义控件

本文参考https://github.com/jenly1314/ArcSeekBar
感谢大佬开源
本文希望实现如图的弧形seekbar进度条,拖动进度条按钮时外侧的刻度条对应颜色变化,中间灯泡颜色也随之变化

Android 弧形SeekBar自定义控件_第1张图片
seekbar.png

需要重写的方法有两个

 protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制弧形
        drawArc(canvas);
       //绘制自定义拖动按钮
        drawThumb(canvas, R.mipmap.seekbar_thumb);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int defaultValue = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,200, getDisplayMetrics());

        int width = measureHandler(widthMeasureSpec,defaultValue);
        int height = measureHandler(heightMeasureSpec,defaultValue);

        //圆心坐标
        mCircleCenterX = (width + getPaddingLeft() - getPaddingRight())/ 2.0f;
        mCircleCenterY = (height + getPaddingTop() - getPaddingBottom())/ 2.0f;
        //计算间距
        int padding = Math.max(getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom());
        //半径 = 视图宽度-横向或纵向内间距值 - 画笔宽度
        mRadius = (height - padding) / 2.0f - Math.max(mStrokeWidth, mThumbStrokeWidth) - mTickStrokeWidth;

        //自定义着色器
        //着色器颜色起始位置
        mArcPosition = new float[]{0.14f, 0.36f, 1f};
        mSweepShader = new SweepGradient(mCircleCenterX, mCircleCenterY, mArcColors, mArcPosition);

        setMeasuredDimension(width,height);
    }

绘制弧形的代码如下

    /**
     * 绘制弧形(默认为一个圆)
     * @param canvas
     */
    private void drawArc(Canvas canvas){
        mPaint.reset();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);

        if(isShowTick){//是否显示刻度
            mPaint.setStrokeWidth(mTickStrokeWidth);
            float circleRadius =  mRadius + mTickPadding + Math.max(mStrokeWidth, mThumbStrokeWidth);
            float tickDiameter = circleRadius * 2;
            float tickStartX = mCircleCenterX - circleRadius;
            float tickStartY = mCircleCenterY - circleRadius;
            RectF rectF = new RectF(tickStartX, tickStartY,tickStartX + tickDiameter,tickStartY + tickDiameter);

            final int currentBlockIndex = (int)(mProgressPercent / 100f * mTotalTickCount);

            for (int i = 0; i < mTotalTickCount; i++) {
                if (i < currentBlockIndex) {
                    //已选中的刻度
                    if (isShader && mSweepShader != null) {
                        mPaint.setShader(mSweepShader);
                    } else {
                        mPaint.setColor(mProgressColor);
                    }
                    //绘制刻度
                    canvas.drawArc(rectF, i * (mBlockAngle + mTickSplitAngle) + mStartAngle, mBlockAngle, false, mPaint);
                } else {
                    if(mNormalColor != 0){
                        //未选中的刻度
                        mPaint.setShader(null);
                        mPaint.setColor(mNormalColor);
                        //绘制刻度
                        canvas.drawArc(rectF, i * (mBlockAngle + mTickSplitAngle) + mStartAngle, mBlockAngle, false, mPaint);
                    }
                }
            }

        }

        mPaint.setStrokeWidth(mStrokeWidth);
        mPaint.setShader(null);
        mPaint.setStrokeCap(mStrokeCap);

        //进度圆半径
        float diameter = mRadius * 2;
        float startX = mCircleCenterX - mRadius;
        float startY = mCircleCenterY - mRadius;
        RectF rectF1 = new RectF(startX, startY,startX + diameter,startY + diameter);

        if(mSweepShader != null){
            mPaint.setShader(mSweepShader);
            //绘制底层弧形
            canvas.drawArc(rectF1, mStartAngle, mSweepAngle,false, mPaint);
        }

        //着色器不为空则设置着色器,反之用纯色
        mPaint.setColor(mProgressColor);

        float ratio = getRatio();
        if(ratio != 0){
            //绘制当前进度弧形
            canvas.drawArc(rectF1, mStartAngle, mSweepAngle * ratio,false, mPaint);
        }
    }

这里着色器颜色为float类型数组,8位色值

private int[] mShaderColors = new int[]{0xFF4FEAAC,0xFFA8DD51,0xFFE8D30F,0xFFA8DD51,0xFF4FEAAC};

绘制拖动按钮的代码如下

private void drawThumb(Canvas canvas, int resId){
        DisplayMetrics displayMetrics = getDisplayMetrics();
        //中央圆形
        float centerRadius =  TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,22, displayMetrics);

        if(isShowThumb){
            mPaint.reset();
            mPaint.setAntiAlias(true);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeCap(Paint.Cap.ROUND);
            mPaint.setStrokeWidth(mThumbStrokeWidth);

            mIconPaint.reset();
            mIconPaint.setAntiAlias(true);
            mIconPaint.setStyle(Paint.Style.FILL);
            mIconPaint.setColor(getColor(mArcColors[1], mArcColors[2], getProgressPercent()/100f));


            float thumbAngle = mStartAngle + mSweepAngle * getRatio();
            //已知圆心,半径,角度,求圆上的点坐标
            mThumbCenterX = (float) (mCircleCenterX  + mRadius * Math.cos(Math.toRadians(thumbAngle)));
            mThumbCenterY = (float) (mCircleCenterY + mRadius * Math.sin(Math.toRadians(thumbAngle)));
           //拖动按钮绘制
            Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), resId);
            //平移偏移量
            float dx = mThumbCenterX - bitmap.getWidth()/2f;
            float dy = mThumbCenterY - bitmap.getHeight()/2f;
            Matrix m=new Matrix();
            m.postTranslate(dx, dy);
            m.postRotate(thumbAngle, mThumbCenterX, mThumbCenterY);

            if(isCanDrag){
                canvas.drawBitmap(bitmap, m, mPaint);
                if (isShowCenterIcon) canvas.drawCircle(mCircleCenterX, mCircleCenterY - 22, centerRadius, mIconPaint);
//                canvas.drawCircle(mThumbCenterX, mThumbCenterY,mThumbRadius + mThumbRadiusEnlarges, mPaint);
            }else{
                canvas.drawBitmap(bitmap, m, mPaint);
                if (isShowCenterIcon) canvas.drawCircle(mCircleCenterX, mCircleCenterY - 22, centerRadius, mIconPaint);
//                canvas.drawCircle(mThumbCenterX, mThumbCenterY,mThumbRadius,mPaint);
            }
        }
    }

特别注意:drawBitmap方法用法
在上述代码中,采用的是传入matrix矩阵的方法,这个方法可以这样理解,首先将bitmap绘制在屏幕左上角,使其左上角坐标为(0,0),接着通过matrix矩阵对bitmap进行平移和旋转操作

m.postTranslate(dx, dy);
m.postRotate(thumbAngle, mThumbCenterX, mThumbCenterY);

通过上述两个方法设置图片的平移和旋转
同时在绘制拖动按钮的过程中,控制中央图标的颜色随之变化

 /**
     *  取两个颜色间的渐变区间中某百分比位置的颜色
     * @param startColor
     * @param endColor
     * @param radio
     * @return
     */
    public int getColor(int startColor, int endColor, float radio) {
        int redStart = Color.red(startColor);
        int blueStart = Color.blue(startColor);
        int greenStart = Color.green(startColor);
        int redEnd = Color.red(endColor);
        int blueEnd = Color.blue(endColor);
        int greenEnd = Color.green(endColor);

        int red = (int) (redStart + ((redEnd - redStart) * radio + 0.5));
        int greed = (int) (greenStart + ((greenEnd - greenStart) * radio + 0.5));
        int blue = (int) (blueStart + ((blueEnd - blueStart) * radio + 0.5));
        return Color.argb(255, red, greed, blue);
    }

上述代码可以根据当前渐变起止颜色和进度确定某百分比位置的颜色

你可能感兴趣的:(Android 弧形SeekBar自定义控件)