自定义圆环百分占比view (label文字支持自动换行 和设置显示行数)

按照国际惯例,先上效果图

1.png

2.png

3.png

数据结构

从图可以看出我们需要标志类型的label 数量value 所占的角度angle 还有代表的颜色,得出数据结构如下

public static class Entry implements Comparable {
   
        public String label;
        public int value;
        public float angle;
        public int color;

        public Entry(String label, int value) {
            this.label = label;
            this.value = value;
        }

        @Override
        public int compareTo(@NonNull Object o) {
            Entry e = (Entry) o;
            if (value > e.value)
                return -1;
            if (value < e.value)
                return 1;
            return 0;
        }
    }

需求分析

按照数据总数平分一个圆,但是可能存在不能整分的情况,还有可能分的角度太小都看不到。

  • 最小角度为2,小于2度的设置成2度,方便查看
  • 按照Entry 的value 值分配角度e.angle = (360.0f * e.value) / count + arrearage;(count总个数,arrearage为上一个亏欠的度数)
  • 上面亏欠的度数arrearage = e.angle - 2f (<0) ,由下一个项目补偿
  • 如果全部计算完毕之后arrearage < 0 ,既还有欠费,那么再循环一遍,重新分配一次
  • 为了简化程序,arrearage 亏欠补偿是由下一个补偿的,没有考虑平均分摊,而且只有补偿之后e.angle + arrearage > 2 角度仍然大于2度的才有资格替上面一个补偿亏欠

代码

注释已经写的很清楚了,这里就不再解释,具体的坐标计算了,里面包括了一些数学的东西,椭圆的知识忘了可以百度一下,还有解决了TextPaint 绘制文字重叠不自动换行的问题,具体参考 Canvas的drawText绘制文本自动换行(支持设置显示最大行数)

自定义view : PieChart.java

public class PieChart extends View {
   

    ArrayList<Entry> mDataSet = new ArrayList<>();
    Paint mPaint;
    //写小圆文字 自动换行和限制最大行数
    private TextPaint mTextPain;
    private static final int MAX_LINE = 2;

    public PieChart(Context context) {
        super(context);
        init();
    }

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

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

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public PieChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    /**
     *
     * @param data 由于CompanyInfo 限制,最多只有 6个数据项
     * @param centerText
     */
    public void setData(ArrayList<Entry> data, String centerText) {
        mDataSet.clear();
        if (data != null) {
            mDataSet.addAll(data);
        }
        mCenterText = centerText;
        preCalcute();
        this.requestLayout();
    }


    public void setData(String[] labels, int values[], String centerText) {
        mDataSet.clear();
        for (int i = 0; i < labels.length; i++) {
            mDataSet.add(new Entry(labels[i], values[i]));
        }
        mCenterText = centerText;
        preCalcute();
        this.requestLayout();
    }


    // 计算角度,颜色
    private void preCalcute() {
        //计算 颜色(最大->最小 按COLORS数字依次分配,所以先排序
        ArrayList<PieChart.Entry> tmp = new ArrayList<>(mDataSet.size());
        tmp.addAll(mDataSet);

        Collections.sort(tmp);

        int i = 0;
        for (Entry e : tmp) {
            e.color = COLORS[i++];
        }

        if (mSortData) {
            Collections.sort(mDataSet);
        }

        int count = 0;
        for (Entry e : mDataSet) {
            count += e.value;
        }

        mCount = count;
        //计算角度
        float arrearage = 0;//用于补偿的中间变量,初始化0
        for (Entry e : mDataSet) {
            e.angle = (360.0f * e.value) / count + arrearage;
            // 角度太小,就画不出来了,所以设置最小角度为2,把多占用的让下一个承担
            if (e.angle < 2f) {
                arrearage = e.angle - 2f;//这是欠的度数
                e.angle = 2;
            } else {
                arrearage = 0;
            }
        }
        if (arrearage < 0) {
  //最后还有欠费,就再循环一遍,让大家(下一个)分担下欠费
            for (Entry e : mDataSet) {
                if (e.angle + arrearage > 2) {
  //如果分担后仍然大于2,就让他分担
                    e.angle += arrearage;
                    break;
                }
            }
        }

        Locale locale = Locale.getDefault();
        if (tmp.size() < 6) {
  //下面的小圆小于6个
            singleItemWidth = convertDpToPixel(63);
        } else {
            singleItemWidth = convertDpToPixel(56);
        }

        if (locale.getLanguage().toLowerCase().startsWith("zh")) {
            textSizeLabel = convertDpToPixel(14);
        } else {
            if (tmp.size() < 6) {
                textSizeLabel = convertDpToPixel(13);
            } else {
                textSizeLabel = convertDpToPixel(11);
            }
        }
    }

    //是否需要对数据进行排序
    boolean mSortData;

    public void setSort(boolean sort) {
        mSortData = sort;
    }

    //预制颜色,从大到小
    final int COLORS[] = {
  0xFF00A7CF, 0xFF8E7BE6, 0xFF0179B1, 0xFF73AC1A, 0xFFF5B910<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值