自定义弧形

本文详细介绍了如何在编程中实现自定义弧形效果,包括使用不同库和方法来绘制弧形,以及调整弧度、颜色和填充等属性,适用于前端和图形界面应用的开发者。
摘要由CSDN通过智能技术生成


public class ProgressViewNew extends View {

    private static final int DEFAULT_MIN_WIDTH = 200; //View默认大小
    //圆弧的宽度
    private static final float DEFAULT_BORDER_WIDTH = 20f;
    //小原点的半径
    private int  DEFAULT_LITLE_WIDTH = dipToPx(5);

    private ValueAnimator progressAnimator;
    private float textSize = getResources().getDimension(R.dimen.x24);
    private float BIM_textSize = getResources().getDimension(R.dimen.x28);
    private float Number_textSize = getResources().getDimension(R.dimen.x60);
    private float padding=getResources().getDimension(R.dimen.x12);//默认胖和瘦距离上面圆环的距离
    private int marging=(int)(Math.max(DEFAULT_BORDER_WIDTH,textSize));
    private float currentAngle=0;
    private String PANG_NUMBER="";
    //当前的分数
    private float maxCount=50;
    private float currentCount=0;
    private float startAngle = -190;//开始绘制的角度
    private float lastAngle=10;//最后绘制的角度

    private int aniSpeed = 1000;//设置滚动的速度

    DecimalFormat df = new DecimalFormat("0.0");//格式化小数,不足的补0
    //分段颜色
//    private int[] colors = new int[]{Color.GREEN, Color.YELLOW, Color.RED, Color.RED};
//    private int[] colors = new int[]{Color.parseColor("#f19c9c"),
//            Color.parseColor("#e4393c")};
    private int[] colors = new int[]{getResources().getColor(R.color.white),
            getResources().getColor(R.color.white)};
    /**
     * 填充色主要参数:
     colors[]
     positions[]
     即每个position定义一个color值,注意position是一个相对位置,其值应该在0.0到1.0之间。
     18.5/24.0/28.0/35.0
     */
    private float positions[]=new float[colors.length];
    //分割区域的数值
    private double position_line[] = new double[]{0,18.5,24.0,28.0,35.0};

    public ProgressViewNew(Context context) {
        super(context);
    }

    public ProgressViewNew(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //1------绘制默认灰色的圆弧
//        float r = Math.min(getHeight() / 2, getWidth() / 2);//半径
        float r = (float) (getResources().getDimension(R.dimen.x220)/Math.cos(new Double(10/180)));//半径
        Paint paintDefalt = new Paint();
        paintDefalt.setColor(getResources().getColor(R.color.white));
        //设置笔刷的样式 Paint.Cap.Round ,Cap.SQUARE等分别为圆形、方形
        paintDefalt.setStrokeCap(Paint.Cap.SQUARE);
        float borderWidth = DEFAULT_BORDER_WIDTH;//圆弧宽度

        float centerX=(getHeight()-2*marging) / 2;
//        RectF oval1=new RectF(marging,marging,getWidth()-marging,getHeight()-marging);
        RectF oval1=new RectF(marging,marging,2*r+marging,2*r+marging);
        /**
         * Paint.Style.FILL    :填充内部
         Paint.Style.FILL_AND_STROKE  :填充内部和描边
         Paint.Style.STROKE  :仅描边
         */
        paintDefalt.setStyle(Paint.Style.STROKE);//设置填充样式
        paintDefalt.setAntiAlias(true);//抗锯齿功能
        paintDefalt.setStrokeWidth(borderWidth);//设置画笔宽度
//        该类是第二个参数是角度的开始,第三个参数是多少度
//        canvas.drawCircle(getWidth() / 2, getHeight() / 2, r, paintCircle);//画圆,圆心在中心位置,半径为长宽小者的一半

        /**
         * drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//画弧,
         参数一是RectF对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,
         参数二是起始角(度)在电弧的开始,
         参数三扫描角(度)开始顺时针测量的,参数四是如果这是真的话,包括椭圆中心的电弧,并关闭它,如果它是假这将是一个弧线,参数五是Paint对象;
         */
        //绘制默认灰色的圆弧
        canvas.drawArc(oval1, -190, 200, false, paintDefalt);//小弧形
//        参数一为渐变起初点坐标x位置,参数二为y轴位置,参数三和四分辨对应渐变终点,最后参数为平铺方式,这里设置为镜像.

        //2-----绘制当前进度的圆弧
        Paint paintCurrent = new Paint();
        paintCurrent.setStyle(Paint.Style.STROKE);//设置填充样式
        paintCurrent.setAntiAlias(true);//抗锯齿功能
        paintCurrent.setStrokeWidth(borderWidth);//设置画笔宽度
        //设置笔刷的样式 Paint.Cap.Round ,Cap.SQUARE等分别为圆形、方形
        paintCurrent.setStrokeCap(Paint.Cap.SQUARE);
        /**
         * static final Shader.TileMode CLAMP: 边缘拉伸.
         static final Shader.TileMode MIRROR:在水平方向和垂直方向交替景象, 两个相邻图像间没有缝隙.
         Static final Shader.TillMode REPETA:在水平方向和垂直方向重复摆放,两个相邻图像间有缝隙缝隙.
         */

        LinearGradient lg=new LinearGradient(0,0,100,100,colors,positions, Shader.TileMode.MIRROR);  //渐变颜色
        /**
         @param x0           起始点X坐标
         @param y0           起始点Y坐标
         @param x1           终点X坐标
         @param y1           终点Y坐标
         @param  colors      所有颜色渐变集合
         @param  positions   我们可以让它均匀的渐变,也可以让它按照你想要的比例进行渐变,可以为null,这样的话假设1为整个渐变的长度,我们设置的所有颜色(假设有4种颜色),都以同等的权重(渐变长度比例0.25:0.25:0.25:0.25)进行颜色渐变。
         @param  tile        着色器的不同模式
         */
        // 创建SweepGradient对象
        // 第一个,第二个参数中心坐标
        // 后面的参数与线性渲染相同

        //18.5/24.0/28.0/35.0
//        positions[0]=Float.parseFloat(df.format(position_line[0]/maxCount));
//        positions[1]=Float.parseFloat(df.format(position_line[1]/maxCount));
//        positions[2]=Float.parseFloat(df.format(position_line[2]/maxCount));
//        positions[3]=Float.parseFloat(df.format(position_line[3]/maxCount));
//        positions[4]=Float.parseFloat(df.format(position_line[4]/maxCount));

        SweepGradient sweepGradient = new SweepGradient(centerX+padding, centerX+padding, Color.parseColor("#f19c9c"), Color.parseColor("#e4393c"));

//        Color.parseColor("#f19c9c"),
//                Color.parseColor("#e4393c")
//        Matrix matrix = new Matrix();
//        matrix.setRotate(130, centerX, centerX);//加上旋转还是很有必要的,每次最右边总是有一部分多余了,不太美观,也可以不加
//        sweepGradient.setLocalMatrix(matrix);
        paintCurrent.setShader(sweepGradient);

//        canvas.drawArc(oval1, 180, currentAngle, false, paintCurrent);//小弧形
        //当前进度startAngle
        canvas.drawArc(oval1, startAngle, currentAngle, false, paintCurrent);
        //3----文字
        //内容显示文字
        Paint vTextPaint = new Paint();
        vTextPaint.setTextSize(textSize);
        vTextPaint.setColor(Color.WHITE);
        vTextPaint.setAntiAlias(true);//抗锯齿功能
//        vTextPaint.setStrokeWidth((float) 3.0);
        vTextPaint.setTextAlign(Paint.Align.CENTER);

        //TODO 等待修改
        canvas.drawText("0", marging, getHeight(), vTextPaint);
        canvas.drawText("MAX", getWidth()-marging, getHeight(), vTextPaint);
        //圆环中心的文字
        vTextPaint.setTextSize(BIM_textSize);
        canvas.drawText("可用额度", getWidth()/2, getHeight()/2-(float)(getResources().getDimension(R.dimen.x28)), vTextPaint);

        //设置肥胖指数
        /**
         * 常用的字体风格名称还有:
         * Typeface.BOLD //粗体
         * Typeface.BOLD_ITALIC //粗斜体
         * Typeface.ITALIC //斜体
         * Typeface.NORMAL //常规
         */
        vTextPaint.setTextSize(Number_textSize);
        Typeface font = Typeface.create(Typeface.SERIF, Typeface.BOLD);
        vTextPaint.setTypeface( font );

        canvas.drawText("¥ "+PANG_NUMBER, getWidth()/2, getHeight()*2/3, vTextPaint);

        //4---绘制圆弧上的小圆球--根据currentAngle
        /**
         * Paint.Style.FILL    :填充内部
         Paint.Style.FILL_AND_STROKE  :填充内部和描边
         Paint.Style.STROKE  :仅描边
         */
//        canvas.translate(getWidth()/2, getHeight()/2);//这时候的画布已经移动到了中心位置
//        Paint paintCircle = new Paint();
//        paintCircle.setStyle(Paint.Style.FILL);//设置填充样式
//        paintCircle.setAntiAlias(true);//抗锯齿功能
//        paintCircle.setColor(Color.WHITE);
//        paintCircle.setStrokeWidth(borderLitalWidth);//设置画笔宽度

//            canvas.drawCircle((float)(centerX+padding-centerX*Math.cos(currentAngle)), (float)(centerX+padding-centerX*Math.sin(currentAngle)), DEFAULT_LITLE_WIDTH, paintCircle);//画圆,圆心在中心位置,半径为长宽小者的一半
//        canvas.drawCircle(0, 0, DEFAULT_LITLE_WIDTH, paintCircle);//这时候的画布已经移动到了中心位置
        /**
        * 第一个参数为正则顺时针,否则逆时针
        * 后面两个参数是圆心
        * 画布的旋转一定要在,画图形之前进行旋转
        */
//        canvas.rotate(-currentAngle, centerX+padding, centerX+padding);//以圆心旋转的
//        canvas.drawCircle(padding,centerX+padding,DEFAULT_LITLE_WIDTH,paintCircle);
//        canvas.rotate(currentAngle, centerX+padding, centerX+padding);//以圆心旋转的
//        canvas.rotate(currentAngle);
//        canvas.drawCircle(-centerX,0,DEFAULT_LITLE_WIDTH,paintCircle);
//        canvas.rotate(-currentAngle);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));
    }
    /**
     *
     * @param origin
     * @param isWidth
     * @return
     */
    private int measure(int origin, boolean isWidth){
        int result = DEFAULT_MIN_WIDTH;
        int specMode = MeasureSpec.getMode(origin);//得到模式
        int specSize = MeasureSpec.getSize(origin);//得到尺寸

        switch (specMode) {
            //EXACTLY是精确尺寸,当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
            case MeasureSpec.EXACTLY:
                //AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可
            case MeasureSpec.AT_MOST:
                result = specSize;
                if (isWidth) {
//                    widthForUnspecified = result;
                } else {
//                    heightForUnspecified = result;
                }
                break;
            //UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。
            case MeasureSpec.UNSPECIFIED:
            default:
                result = Math.min(result, specSize);
                if (isWidth) {//宽或高未指定的情况下,可以由另一端推算出来 - -如果两边都没指定就用默认值
//                    result = (int) (heightForUnspecified * BODY_WIDTH_HEIGHT_SCALE);
                } else {
//                    result = (int) (widthForUnspecified / BODY_WIDTH_HEIGHT_SCALE);
                }
                if (result == 0) {
                    result = DEFAULT_MIN_WIDTH;
                }
                break;
        }

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

    public void setMaxCount(float maxCount) {
        this.maxCount = maxCount;
    }

    /***
     * 设置当前的进度值
     *
     * @param currentCounts
     */
    public void setCurrentCount(float currentCounts) {
//        this.currentCount = currentCounts > maxCount ? maxCount : currentCounts;
        float curren=Float.parseFloat(df.format(currentCounts/maxCount));//返回的是String类型的
        currentAngle=200*(curren>=1?1:curren);//最后要到达的角度
        PANG_NUMBER= StringUtils.formatQueryAmountNodot(currentCounts+"");//肥胖指数的设置
        invalidate();
//        lastAngle = currentAngle;//保存最后绘制的位置
//        setAnimation(-190, currentAngle, aniSpeed);
    }
    /**
     * 为进度设置动画
     * @param last
     * @param current
     */
    private void setAnimation(float last, float current, int length) {
        progressAnimator = ValueAnimator.ofFloat(last, current);
        Log.e("last=====",last+"");
        Log.e("current=====",current+"");
        progressAnimator.setDuration(length);
//        progressAnimator.setTarget(currentAngle);
        progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
//                currentAngle= (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        progressAnimator.start();
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值