自定义view之半圆环比例图(逻辑简洁,注释详细)

啥也不说先上图,线看效果,代码复制可用

第一:在values下创建 attrs.xml,写需要的自定义属性;
第二:在 自定义View第三个构造方法中,获取自定义属性;
第三:重写onMeasure【不是必须的】;
第四:重写onDraw【所有绘制代码都写在onDraw】;

一,定义线条宽度,间隔背景颜色,间隔角度,两个textview的属性等

<declare-styleable name="RoundRateView">
    <attr name="rrv_circleWidth" format="dimension" />
    <attr name="rrv_intervalColor" format="color" />
    <attr name="rrv_aboveTextColor" format="color" />
    <attr name="rrv_belowTextColor" format="color" />
    <attr name="rrv_intervalAngle" format="dimension" />
    <attr name="rrv_belowTextSize" format="dimension" />
    <attr name="rrv_aboveTextSize" format="dimension" />
    <attr name="rrv_isShowText" format="boolean" />
</declare-styleable>

二,如图,具体看下面代码

三,如图,具体看代码

四,如图,具体看代码

五,是view代码:

public class RoundRateView extends View {
    private int mCircleWidth; //圆环宽度
    private float intervalAngle = 0.5f;//间隔角度
    private int belowTextSize = 60;//下面的文字字体大小
    private int aboveTextSize = 30;//上面的文字字体大小
    private int aboveTextColor = Color.GRAY;//上面的文字 默认灰色
    private int belowTextColor = Color.BLACK;//下面的文字 默认黑色
    private int intervalColor = Color.GRAY;//间隔颜色 默认灰色
    private int noMoneyColor = Color.DKGRAY;//总金额为0 默认灰色
    private boolean isShowText = true; //是否显示中间文字 默认显示
    private Paint mPaint;//圆环画笔
    private Paint textPaint;//文字画笔
    private List<Float> angleList = new ArrayList<>(); //所有的角度 集合
    private List<Integer> colorList = new ArrayList<>(); //所有的色值 集合
    private RectF oval;//圆环所在区域
    private double allMoney;//总金额

    private int colors[] = {Color.parseColor("#41A8FF")
            , Color.parseColor("#86C8FF")
            , Color.parseColor("#FF8B13")
            , Color.parseColor("#FFB971")
            , Color.parseColor("#FF8A77")
            , Color.parseColor("#EEE685")
            , Color.parseColor("#EECBAD")
            , Color.parseColor("#EEAEEE")
            , Color.parseColor("#EE3B3B")
            , Color.parseColor("#EDEDED")};

    public RoundRateView(Context context) {
        super(context);
        init(context, null, 0);
    }

    public RoundRateView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public RoundRateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs,R.styleable.RoundRateView,defStyleAttr,0);
        int n = array.getIndexCount();
        for(int i=0;i<n;i++){
            int attr  = array.getIndex(i);
            switch (attr){//这些属性都在xml里面设置。此处是默认
                case R.styleable.RoundRateView_rrv_circleWidth:
                    mCircleWidth = array.getDimensionPixelSize(attr, (int) dip2px(15));
                    if(mCircleWidth<2){//最小2
                        mCircleWidth = 2;
                    }
                    break;
                case R.styleable.RoundRateView_rrv_intervalAngle:
                    intervalAngle = array.getDimensionPixelSize(attr, (int) dip2px(1));
                    break;
                case R.styleable.RoundRateView_rrv_belowTextSize:
                    belowTextSize = array.getDimensionPixelSize(attr, (int) dip2px(60));
                    break;
                case R.styleable.RoundRateView_rrv_aboveTextSize:
                    aboveTextSize = array.getDimensionPixelSize(attr, (int) dip2px(30));
                    break;
                case R.styleable.RoundRateView_rrv_aboveTextColor:
                    aboveTextColor = array.getColor(attr, Color.GRAY);
                    break;
                case R.styleable.RoundRateView_rrv_belowTextColor:
                    belowTextColor = array.getColor(attr,Color.BLACK);
                    break;
                case R.styleable.RoundRateView_rrv_intervalColor:
                    intervalColor = array.getColor(attr,Color.GRAY);
                    break;
                case R.styleable.RoundRateView_rrv_isShowText:
                    isShowText = array.getBoolean(attr,true);
                    break;
            }
        }
        array.recycle();//定义后属性对象回收

        mPaint = new Paint();//初始化圆环画笔
        mPaint.setAntiAlias(true);//抗锯齿
        mPaint.setStyle(Paint.Style.STROKE); //设置绘画空心(比如画圆时画的是空心圆而不是实心圆)
        mPaint.setStrokeWidth(mCircleWidth);//设置画笔线宽

        textPaint = new Paint();//文字画笔
        textPaint.setAntiAlias(true);
        textPaint.setDither(true);//防抖动


    }

    /**
     * 设置数据
     * @param list
     */
    public void setList(List<Double> list) {
        float allIntervalAngle = 0f ;//所有间隔加起来的角度
        float allModuleAngle;  //所有模块加起来的角度  allModuleAngle + allIntervalAngle=180;
        if(list.size()>colors.length){//不能大于定义的颜色数组长度
            return;
        }
        angleList.clear();
        colorList.clear();
        allMoney = 0d;//总资产
        for(int i=0;i<list.size();i++){
            allMoney = allMoney+list.get(i);//资产累加
        }
        if(list.size() == 1){//只有一条数据//如果只有一条数据,就不要间隔
            angleList.add(180f);//半圆
            colorList.add(colors[0]);
        }else{
            for(int i=0;i<list.size()-1;i++){
                if(list.get(i)!=0){//数据不为0
                    allIntervalAngle+= intervalAngle; //间隔角度累加
                }
            }
            if(allIntervalAngle == intervalAngle){ //如果只有一条数据不为0,就不要间隔颜色
                angleList.add(180f);
                colorList.add(colors[0]);
            }else{
                allModuleAngle = 180- allIntervalAngle;//所有的颜色模块角度=180-所有间隔角度
                float angle = 0;//每个金额所占角度累加
                for(int i=0;i<list.size();i++){
                    if(list.get(i)!=0){
                        float e = (float) (list.get(i)/allMoney * allModuleAngle);//每个金额所占角度
                        if(i == list.size()-1){//如果是最后一个色块,所占角度就是剩余全部的角度
                            this.angleList.add(allModuleAngle - angle);
                        }else{
                            angle+=e;
                            this.angleList.add(e);
                            this.angleList.add(intervalAngle);
                        }
                        this.colorList.add(colors[i]);
                        this.colorList.add(intervalColor);

                    }
                }
            }

        }
        invalidate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthPixels = this.getResources().getDisplayMetrics().widthPixels;//获取屏幕宽
        int heightPixels = this.getResources().getDisplayMetrics().heightPixels;//获取屏幕高
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int hedight = MeasureSpec.getSize(heightMeasureSpec);
        int minWidth = Math.min(widthPixels, width);//控件宽高不超过屏幕宽高
        int minHedight = Math.min(heightPixels, hedight);//控件宽高不超过屏幕宽高
        setMeasuredDimension(Math.min(minWidth, minHedight), Math.min(minWidth, minHedight));//设置宽高中的最小数为控件的实际宽高
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawCircle(canvas);
        if(isShowText){//是否显示文本
            drawText(canvas);
        }
    }

    private void drawCircle(Canvas canvas) {
        if(oval==null){
            int min = Math.min(getWidth() - mCircleWidth/2,getHeight() - mCircleWidth/2);
            oval = new RectF(mCircleWidth/2,mCircleWidth/2,min,min);
        }
        float startAngle = -180f;//经过试验,-180这个值就是9点方向的位置 -90这个值就是12点方向的位置


        if(allMoney==0){
            mPaint.setColor(noMoneyColor);//总金额为0,给默认颜色
            mPaint.setStrokeWidth(mCircleWidth);
            mPaint.setStrokeCap(Paint.Cap.ROUND);//首位有弧度
            canvas.drawArc(oval, -180, 180, false, mPaint);//sweepAngle 跨度180画半圆  360圆
            return;
        }else{
            //画一个底层圆环,颜色和间隙颜色一样,因为间隙的色块和其他色块之间会有小缝隙
            mPaint.setColor(intervalColor);
            mPaint.setStrokeWidth(mCircleWidth - 1); //宽度减1是防止底色溢出
            mPaint.setStrokeCap(Paint.Cap.BUTT);
            canvas.drawArc(oval, -180, 180, false, mPaint);
            //左边开始的圆弧(没有设置线条开头圆弧,结束矩形的api),所以首位画两个,没找到其他好方法,有大神知道吗请留言回复
            mPaint.setColor(colorList.get(0));
            mPaint.setStrokeWidth(mCircleWidth);
            mPaint.setStrokeCap(Paint.Cap.ROUND);
            canvas.drawArc(oval, -180, 1, false, mPaint);
            //右边结束的圆弧
            mPaint.setColor(colorList.get(angleList.size()-1));
            mPaint.setStrokeWidth(mCircleWidth);
            mPaint.setStrokeCap(Paint.Cap.ROUND);
            canvas.drawArc(oval, 0, 1, false, mPaint);
        }

        mPaint.setStrokeWidth(mCircleWidth);
        for(int i=0;i<angleList.size();i++){
            mPaint.setColor(colorList.get(i));
            mPaint.setStrokeCap(Paint.Cap.BUTT);
//            mPaint.setStrokeCap(Paint.Cap.ROUND);
//            if(i==0 || i==angleList.size()-1){
//                mPaint.setStrokeCap(Paint.Cap.ROUND);
//            }else{
//                mPaint.setStrokeCap(Paint.Cap.BUTT);
//            }
            if(i>0){
                startAngle += angleList.get(i-1);
            }
            canvas.drawArc(oval,startAngle,angleList.get(i),false,mPaint);
        }

    }

    private void drawText(Canvas canvas) {
        int center = getWidth()/2;

        String percent = "总资产(元)";
        textPaint.setTextSize(aboveTextSize);
        //防止文字边界超过内环边界  上面的文字大小减小 下面的文字大小也跟着减小
        while (textPaint.measureText(percent)>getWidth() - 2*mCircleWidth){
            aboveTextSize--;
            belowTextSize--;
            textPaint.setTextSize(aboveTextSize);
        }
        textPaint.setTextAlign(Paint.Align.CENTER);// 设置文字居中,文字的x坐标要注意
        textPaint.setColor(aboveTextColor);
        textPaint.setStrokeWidth(0);// 注意此处一定要重新设置宽度为0,否则绘制的文字会重叠
        Rect bounds = new Rect();// 文字边框
        textPaint.getTextBounds(percent,0,percent.length(),bounds);// 获得绘制文字的边界矩形
        Paint.FontMetricsInt fontMetricsInt = textPaint.getFontMetricsInt();// 获取绘制Text时的四条线
        int baseline = (int) (center - dip2px(20f));//字体所在位置,在中心点上方20dp
//        int baseline = center+ (fontMetricsInt.bottom - fontMetricsInt.top) / 2 -fontMetricsInt.bottom;
        canvas.drawText(percent,center,baseline,textPaint);

        //总金额
        percent = String.valueOf(allMoney);
        textPaint.setColor(belowTextColor); // 设置文字颜色
        textPaint.setTextSize(belowTextSize);
        textPaint.setFakeBoldText(true);//加粗
        //防止下面的文字超出内环边界
        while (textPaint.measureText(percent)>getWidth()-2f*mCircleWidth) {
            belowTextSize--;
            textPaint.setTextSize(belowTextSize);
        }
        textPaint.getTextBounds(percent, 0, percent.length(), bounds); // 获得绘制文字的边界矩形
        Paint.FontMetricsInt fontMetrics1 = textPaint.getFontMetricsInt(); // 获取绘制Text时的四条线
        int baseline1 = center + (fontMetrics1.bottom - fontMetrics1.top) / 2 - fontMetrics1.bottom;
//        int baseline1 = center + (fontMetrics1.bottom - fontMetrics1.top) / 2 - fontMetrics1.bottom+38*2;
        canvas.drawText(percent, center, baseline1, textPaint); // 绘制文字
    }

    public static float dip2px(float dipValue) {
        final float scale = Resources.getSystem().getDisplayMetrics().density;
        return (dipValue * scale + 0.5f);
    }
    public void setIsShowMoney(boolean isShowMoney) {
        this.isShowText = isShowMoney;
        invalidate();
    }

}

六,使用方法

 

 

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值