Android自定义View, 安全中心安全指数进度条,圆弧进度条

最近新提出需求, 需要自定义一个安全中心页面,表示安全分数的自定义progressbar.效果在最下方贴图.

下面来说下具体的实现方法:

public class ColorArcProgressBar extends View {
    private int diameter = 500;  //直径
    private float centerX;  //圆心X坐标
    private float centerY;  //圆心Y坐标

    private Paint allArcPaint;
    private Paint progressPaint;
    private Paint vTextPaint;
    private Paint hintPaint;
    private Paint degreePaint;
    private Paint curSpeedPaint;

    private RectF bgRect;

    private ValueAnimator progressAnimator;
    private PaintFlagsDrawFilter mDrawFilter;
    private SweepGradient sweepGradient;
    private Matrix rotateMatrix;

    private float startAngle = 155;
    private float sweepAngle = 230;
    private float currentAngle = 0.01f;
    private float lastAngle;
    private int[] colors = new int[]{Color.GREEN, Color.YELLOW, Color.RED, Color.RED};
    private float maxValues = 60;
    private float curValues = 0;
    private float bgArcWidth = dipToPx(10);
    private float progressWidth = dipToPx(10);
    private float textSize = dipToPx(50);
    private float hintSize = dipToPx(9);
    private float curSpeedSize = dipToPx(9);
    private int aniSpeed = 1500;
    private float longDegree = dipToPx(13);
    private float shortDegree = dipToPx(5);
    private final int DEGREE_PROGRESS_DISTANCE = dipToPx(1);

    private String hintColor = "#8d9298";
    private String bgArcColor = "#F0F0F0";
    private String titleString;
    private String hintString;

    private boolean isNeedTitle;
    private boolean isNeedUnit;
    private boolean isNeedDial;
    private boolean isNeedContent;
    private boolean isNeedProgress = false;

    // sweepAngle / maxValues 的值
    private float k;

    public ColorArcProgressBar(Context context) {
        super(context, null);
        initView();
    }

    public ColorArcProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs, 0);
        initCofig(context, attrs);
        initView();
    }

    public ColorArcProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initCofig(context, attrs);
        initView();
    }

    /**
     * 初始化布局配置
     */
    private void initCofig(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ColorArcProgressBar);
        int color1 = a.getColor(R.styleable.ColorArcProgressBar_front_color1, Color.GREEN);
        int color2 = a.getColor(R.styleable.ColorArcProgressBar_front_color2, color1);
        int color3 = a.getColor(R.styleable.ColorArcProgressBar_front_color3, color1);
        int color4 = a.getColor(R.styleable.ColorArcProgressBar_front_color4, color1);
        int color5 = a.getColor(R.styleable.ColorArcProgressBar_front_color5, color1);
        colors = new int[]{color1, color2, color3, color4, color5, color5};

        sweepAngle = a.getInteger(R.styleable.ColorArcProgressBar_total_engle, 230);
        bgArcWidth = a.getDimension(R.styleable.ColorArcProgressBar_back_width, dipToPx(10));
        progressWidth = a.getDimension(R.styleable.ColorArcProgressBar_front_width, dipToPx(10));
        isNeedTitle = a.getBoolean(R.styleable.ColorArcProgressBar_is_need_title, false);
        isNeedContent = a.getBoolean(R.styleable.ColorArcProgressBar_is_need_content, false);
        isNeedUnit = a.getBoolean(R.styleable.ColorArcProgressBar_is_need_unit, false);
        isNeedDial = a.getBoolean(R.styleable.ColorArcProgressBar_is_need_dial, false);
        hintString = a.getString(R.styleable.ColorArcProgressBar_string_unit);
        titleString = a.getString(R.styleable.ColorArcProgressBar_string_title);
        curValues = a.getFloat(R.styleable.ColorArcProgressBar_current_value, 0);
        maxValues = a.getFloat(R.styleable.ColorArcProgressBar_max_value, 60);
        setCurrentValues(curValues);
        setMaxValues(maxValues);
        a.recycle();

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = (int) (2 * longDegree + progressWidth + diameter + 2 * DEGREE_PROGRESS_DISTANCE);
        int height = (int) (2 * longDegree + progressWidth + diameter + 2 * DEGREE_PROGRESS_DISTANCE);
        setMeasuredDimension(width, height);
    }

    private void initView() {

        diameter = 3 * getScreenWidth() / 5;
        //弧形的矩阵区域
        bgRect = new RectF();
        bgRect.top = longDegree + progressWidth / 2 + DEGREE_PROGRESS_DISTANCE;
        bgRect.left = longDegree + progressWidth / 2 + DEGREE_PROGRESS_DISTANCE;
        bgRect.right = diameter + (longDegree + progressWidth / 2 + DEGREE_PROGRESS_DISTANCE);
        bgRect.bottom = diameter + (longDegree + progressWidth / 2 + DEGREE_PROGRESS_DISTANCE);

        //圆心
        centerX = (2 * longDegree + progressWidth + diameter + 2 * DEGREE_PROGRESS_DISTANCE) / 2;
        centerY = (2 * longDegree + progressWidth + diameter + 2 * DEGREE_PROGRESS_DISTANCE) / 2;

        //外部刻度线
        degreePaint = new Paint();
        degreePaint.setAntiAlias(true);
        degreePaint.setColor(Color.parseColor("#C8C8C8"));
        degreePaint.setStrokeWidth(dipToPx(1f));
        degreePaint.setTextSize(dipToPx(10f));
        //整个弧形
        allArcPaint = new Paint();
        allArcPaint.setAntiAlias(true);
        allArcPaint.setStyle(Paint.Style.STROKE);
        allArcPaint.setStrokeCap(Paint.Cap.ROUND);
        allArcPaint.setStrokeWidth(bgArcWidth);
        allArcPaint.setColor(Color.parseColor(bgArcColor));

        //当前进度的弧形
        progressPaint = new Paint();
        progressPaint.setAntiAlias(true);
        progressPaint.setStyle(Paint.Style.STROKE);
        progressPaint.setStrokeCap(Paint.Cap.ROUND);
        progressPaint.setStrokeWidth(progressWidth);
        progressPaint.setColor(Color.GREEN);

        //内容显示文字
        vTextPaint = new Paint();
        vTextPaint.setTextSize(textSize);
        vTextPaint.setColor(Color.BLACK);
        vTextPaint.setTextAlign(Paint.Align.CENTER);

        //显示单位文字
        hintPaint = new Paint();
        hintPaint.setTextSize(hintSize);
        hintPaint.setColor(Color.parseColor(hintColor));
        hintPaint.setTextAlign(Paint.Align.CENTER);

        //显示标题文字
        curSpeedPaint = new Paint();
        curSpeedPaint.setTextSize(curSpeedSize);
        curSpeedPaint.setColor(Color.WHITE);
        curSpeedPaint.setTextAlign(Paint.Align.CENTER);

        mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
        sweepGradient = new SweepGradient(centerX, centerY, colors, null);
        rotateMatrix = new Matrix();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        //抗锯齿
        canvas.setDrawFilter(mDrawFilter);

        if (isNeedDial) {
            //画刻度线 TODO 画刻度线可以优化
            for (int i = 0; i < 156; i++) {
                if (i < 28 || i > 128) {
                    canvas.rotate(2.30769f, centerX, centerY);
                    continue;
                }
                canvas.drawLine(centerX, centerY + diameter / 2 - progressWidth / 2 - DEGREE_PROGRESS_DISTANCE - (longDegree - shortDegree) / 2,
                        centerX, centerY + diameter / 2 - progressWidth / 2 - DEGREE_PROGRESS_DISTANCE - (longDegree - shortDegree) / 2 - shortDegree, degreePaint);
                if (((i - 28) % 20) == 0) {
                    if (i == 128) {
                        canvas.rotate(-2.30769f * 2, centerX, centerY);
                    } else {
                        canvas.rotate(-2.30769f, centerX, centerY);
                    }
                    canvas.rotate(180f, centerX, centerY + diameter / 2 - progressWidth / 2 - DEGREE_PROGRESS_DISTANCE - (longDegree - shortDegree) / 2 - shortDegree - dipToPx(10));
                    canvas.drawText(String.valueOf(i - 28), centerX, centerY + diameter / 2 - progressWidth / 2 - DEGREE_PROGRESS_DISTANCE - (longDegree - shortDegree) / 2 - shortDegree - dipToPx(10), degreePaint);
                    canvas.rotate(-180f, centerX, centerY + diameter / 2 - progressWidth / 2 - DEGREE_PROGRESS_DISTANCE - (longDegree - shortDegree) / 2 - shortDegree - dipToPx(10));
                    if (i == 128) {
                        canvas.rotate(2.30769f * 2, centerX, centerY);
                    } else {
                        canvas.rotate(2.30769f, centerX, centerY);
                    }
                }
                canvas.rotate(2.30769f, centerX, centerY);
            }
        }

        //整个弧
        canvas.drawArc(bgRect, startAngle, sweepAngle, false, allArcPaint);

        //设置渐变色
        rotateMatrix.setRotate(150, centerX, centerY);
        sweepGradient.setLocalMatrix(rotateMatrix);
        progressPaint.setShader(sweepGradient);

        //当前进度
        if (isNeedProgress) {
            canvas.drawArc(bgRect, startAngle, currentAngle, false, progressPaint);
        }

        if (isNeedContent) {
            canvas.drawText(String.format("%.0f", curValues), centerX, centerY, vTextPaint);
        }
        if (isNeedUnit) {
            canvas.drawText(hintString, centerX, centerY + textSize / 2.6f, hintPaint);
        }
        if (isNeedTitle) {
            RectF rectF = new RectF();
            rectF.left = centerX - dipToPx(32);
            rectF.top = centerY + textSize / 1.1f - dipToPx(11);
            rectF.right = centerX + dipToPx(32);
            rectF.bottom = centerY + textSize / 1.1f + dipToPx(4);
            canvas.drawRoundRect(rectF, dipToPx(15), dipToPx(15), vTextPaint);        //绘制圆角矩形
            canvas.drawText(titleString, centerX, centerY + textSize / 1.1f, curSpeedPaint);
        }

        invalidate();

    }

    /**
     * 设置最大值
     */
    public void setMaxValues(float maxValues) {
        this.maxValues = maxValues;
        k = sweepAngle / maxValues;
    }

    /**
     * 设置当前值
     */
    public void setCurrentValues(float currentValues) {
        if (currentValues > maxValues) {
            currentValues = maxValues;
        }
        if (currentValues < 0) {
            currentValues = 0;
        }
        this.curValues = currentValues;
        lastAngle = currentAngle;
        setAnimation(lastAngle, currentValues * k, aniSpeed);
    }

    /**
     * 设置整个圆弧宽度
     */
    public void setBgArcWidth(int bgArcWidth) {
        this.bgArcWidth = bgArcWidth;
    }

    /**
     * 设置进度宽度
     */
    public void setProgressWidth(int progressWidth) {
        this.progressWidth = progressWidth;
    }

    /**
     * 设置速度文字大小
     */
    public void setTextSize(int textSize) {
        this.textSize = textSize;
    }

    /**
     * 设置单位文字大小
     */
    public void setHintSize(int hintSize) {
        this.hintSize = hintSize;
    }


    public void setNeedProgress(boolean needProgress) {
        isNeedProgress = needProgress;
    }

    /**
     * 设置单位文字
     */
    public void setUnit(String hintString) {
        this.hintString = hintString;
        invalidate();
    }

    /**
     * 设置直径大小
     */
    public void setDiameter(int diameter) {
        this.diameter = dipToPx(diameter);
    }

    /**
     * 设置标题
     */
    private void setTitle(String title) {
        this.titleString = title;
    }

    /**
     * 设置是否显示标题
     */
    private void setIsNeedTitle(boolean isNeedTitle) {
        this.isNeedTitle = isNeedTitle;
    }

    /**
     * 设置是否显示单位文字
     */
    private void setIsNeedUnit(boolean isNeedUnit) {
        this.isNeedUnit = isNeedUnit;
    }

    /**
     * 设置是否显示外部刻度盘
     */
    private void setIsNeedDial(boolean isNeedDial) {
        this.isNeedDial = isNeedDial;
    }

    /**
     * 为进度设置动画
     */
    private void setAnimation(final float last, final float current, int length) {
        progressAnimator = ValueAnimator.ofFloat(last, current);
        progressAnimator.setDuration(length);
        progressAnimator.setTarget(currentAngle);
        progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currentAngle = (float) animation.getAnimatedValue();
                curValues = currentAngle / k;
                if (vTextPaint != null) {
                    if (0 <= curValues && curValues < 20f) {
                        vTextPaint.setColor(Color.parseColor("#FB8301"));
                        titleString = "安全等级低";
                    } else if (20f <= curValues && curValues < 40f) {
                        vTextPaint.setColor(Color.parseColor("#FBB004"));
                        titleString = "安全等级低";
                    } else if (40f <= curValues && curValues < 60f) {
                        vTextPaint.setColor(Color.parseColor("#C9C730"));
                        titleString = "安全等级中";
                    } else if (60f <= curValues && curValues < 80f) {
                        vTextPaint.setColor(Color.parseColor("#71CC80"));
                        titleString = "安全等级中";
                    } else {
                        vTextPaint.setColor(Color.parseColor("#0598F6"));
                        titleString = "安全等级高";
                    }
                }
            }
        });
        progressAnimator.start();
    }

    /**
     * dip 转换成px
     */
    private int dipToPx(float dip) {
        float density = getContext().getResources().getDisplayMetrics().density;
        return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
    }

    /**
     * 得到屏幕宽度
     */
    private int getScreenWidth() {
        WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.widthPixels;
    }
}

1.其中再为进度设置动画时,需改变安全等级背景颜色方法较笨, 应该能进一步实现背景颜色随进度条走到的颜色变而变化, 知道的小伙伴,可以在评论中给出答案噢~感谢~
2.在刚开始currentAngle 设置为0.01f,是为了适配oppo手机开始会显示进度条100%的闪烁, 改成0.01f就不会有这种bug了.属于笨方法解决哦~

贴出sttr.xml文件:

 <declare-styleable name="ColorArcProgressBar">
        <attr name="back_color" format="color"/>
        <attr name="front_color1" format="color"/>
        <attr name="front_color2" format="color"/>
        <attr name="front_color3" format="color"/>
        <attr name="front_color4" format="color"/>
        <attr name="front_color5" format="color"/>
        <attr name="back_width" format="dimension"/>
        <attr name="front_width" format="dimension"/>
        <attr name="is_need_title" format="boolean"/>
        <attr name="is_need_content" format="boolean"/>
        <attr name="is_need_unit" format="boolean"/>
        <attr name="is_need_dial" format="boolean"/>
        <attr name="string_title" format="string"/>
        <attr name="string_unit" format="string"/>
        <attr name="total_engle" format="integer"/>
        <attr name="current_value" format="float"/>
        <attr name="max_value" format="float"/>

    </declare-styleable>

在xml中使用:

 <com.clwapp.hx.widget.ColorArcProgressBar
                    android:id="@id/color_arc_progressbar"
                    android:layout_width="200dp"
                    android:layout_height="200dp"
                    android:layout_centerHorizontal="true"
                    android:layout_marginTop="@dimen/common_13dp"
                    app:back_color="@android:color/darker_gray"
                    app:back_width="10dp"
                    app:front_color1="#FB7E00"
                    app:front_color2="#FAC405"
                    app:front_color3="#73CC7C"
                    app:front_color4="#06A2F3"
                    app:front_color5="#0079FC"
                    app:front_width="10dp"
                    app:is_need_content="true"
                    app:is_need_dial="true"
                    app:is_need_title="true"
                    app:is_need_unit="true"
                    app:max_value="100"
                    app:string_title="安全等级低"
                    app:string_unit="BETA"
                    app:total_engle="230" />

在Activity中使用:

progressBar.setNeedProgress(true);
progressBar.setCurrentValues(80);

这样, 自定义progressbar就写完了, 有不足的地方,还请各大神门指正!!!

效果如图:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值