自定义圆环百分占比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, 0xFFA5BBD1,
            0xFF00A7CF, 0xFF8E7BE6, 0xFF0179B1, 0xFF73AC1A, 0xFFF5B910, 0xFFA5BBD1,};

    //中心的文字“总量”多语言由外面传入
    String mCenterText;
    //总量
    int mCount;

    private void init() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mTextPain = new TextPaint();
        mTextPain.setAntiAlias(true);
    }

    // 上下padding
    float padding = convertDpToPixel(12);
    // 大圆的半径
    float radius = convertDpToPixel(65);
    // 圆环的宽度
    float border = radius / 4;
    // 小圆半径
    float sradius = convertDpToPixel(15);

    //大圆心 数字大小
    float textSizeBigCount = convertDpToPixel(18);
    //大圆心 文字大小
    float textSizeBigCircle2 = convertDpToPixel(20);
    // label文字大小
    float textSizeLabel = convertDpToPixel(14);
    // label数字字大小
    float textSizeSmallCount = convertDpToPixel(12);
    //线条的宽度
    float lineStrokeWidth = convertDpToPixel(1);
    //阴影的半径
    float shadownRadisu = convertDpToPixel(2);
    //总量的间距
    float paddingTotal = convertDpToPixel(5);
    //下面圆与上面圆的间距
    float paddingBottomCircle = convertDpToPixel(15);
    //下面每一個item的寬度
    float singleItemWidth = convertDpToPixel(61);

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //背景涂成白色
        canvas.drawColor(0xffffffff);
        int width = getWidth();
        int height = getHeight();

        //计算圆心,
        float cx = width / 2;
        float cy = radius + padding;//加上顶部偏移,美观一点

        mPaint.setColor(Color.WHITE);
        float strokeWidth = mPaint.getStrokeWidth();

        //绘制圆环的阴影
//        setLayerType(LAYER_TYPE_SOFTWARE,mPaint);
        mPaint.setShadowLayer(shadownRadisu, 0, shadownRadisu, 0xffAAAAAA);//第一个参数为模糊半径,越大越模糊。 第二个参数是阴影离开文字的x横向距离。 第三个参数是阴影离开文字的Y横向距离。 第四个参数是阴影颜色
        mPaint.setStrokeWidth(border + shadownRadisu / 2);//圆环的着色宽度  1/4大圆环半径的+1/2 外部阴影宽度
        mPaint.setColor(Color.WHITE);
        mPaint.setStyle(Paint.Style.STROKE);//白色 描边
        //内部圆环 + 圆环 +阴影
        canvas.drawCircle(cx, cy, radius - border / 2, mPaint);

        //取消阴影
        mPaint.clearShadowLayer();
        //恢复线框宽度
        mPaint.setStyle(Paint.Style.FILL);

        //对于如果只有2个数据,绘制线条, label
        if (mDataSet.size() == 2) {
            //绘制线条
            float ex = 0, ey = 0;
            // 椭圆方程 (x/a)^2+(y/b)^2=1
            float b = radius + border / 4;//比圆多1/16半径做椭圆短轴
            float a = radius + border;//比圆多1/4半径做椭圆长轴
         /*  paint.setColor(Color.RED);
            for(float x=-a;x<a;x++){
                float y =(float) Math.sqrt(( 1 - x*x/(a*a))*b*b);
                canvas.drawPoint(cx+x,cy+y,paint);
                canvas.drawPoint(cx+x,cy-y,paint);
            }*/
            mPaint.setStrokeWidth(lineStrokeWidth);

            //绘制 2个 线条和文字label
            float tmp = 0;
            for (int i = 0; i < mDataSet.size(); i++) {
                Entry e = mDataSet.get(i);
                int sg = 1;
                //所占圆环中心角度 加上上一个角度如果小于180 画右边设置右对齐
                //这个是为了让上下两个图的文字靠近圆环端能够对齐在一条直线上
                if (tmp + e.angle / 2 > 180) {
                    sg = -1;
                    mPaint.setTextAlign(Paint.Align.RIGHT);
                } else {
                    mPaint.setTextAlign(Paint.Align.LEFT);
                }
                float angle = e.angle;
                if (angle > 180) {
                    angle = 240;
                } else {
                    if (angle > 120)
                        angle = 120;
                }
                //圆环上所占区域中心点到原点 与Y轴的夹角
                angle = (float) ((angle / 2) * Math.PI / 180);
                float k2 = (float) (1 / Math.tan(angle));

                //圆环上所占区域中心点到原点直线与 椭圆的交点坐标
                ex = (float) Math.sqrt(1 / (1 / (a * a) + (k2 * k2) / (b * b)));
                ey = k2 * ex;

                //圆环上所占区域中心点到原点直线与 圆的交点坐标
                float sx = cx + sg * (float) (radius * Math.sin(angle));
                float sy = cy - (float) (radius * Math.cos(angle));

                //最边上(左右边)到文字对齐的线的距离(默认大小为 半径 + 1/8 半径) 既给文字 + 横线(1/8 半径)  留出的宽度
                float linepad = radius + border * 2;

                mPaint.setColor(e.color);
                //圆环上的点到椭圆上的点的斜线
                canvas.drawLine(sx, sy, cx + sg * ex, cy - ey, mPaint);
                //椭圆上的点到文字边上的 横线(border * 2)
                canvas.drawLine(cx + sg * ex, cy - ey, cx + sg * (linepad), cy - ey, mPaint);
                //画文字描述(这里太长会跑出界面,如果需要处理,请参考下面小圆文字的处理)
                mPaint.setColor(0xff5f5f5f);
                mPaint.setTextSize(textSizeLabel);
                canvas.drawText(e.label, cx + sg * (linepad) + sg * textSizeLabel / 2, cy - ey + textSizeLabel * 1 / 3, mPaint);
                tmp += e.angle;//下一个圆环区域中心点的起始角度
            }
        }

        //再绘制白色圆环,把外边缘的白色漏出来(再画一次,)
        mPaint.setColor(Color.WHITE);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(border + shadownRadisu / 2);
        canvas.drawCircle(cx, cy, radius - border / 2, mPaint);

        mPaint.setStrokeWidth(strokeWidth);

        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        canvas.save();
        //剪切内小圆,再绘制扇形,就成了扇形圆环
        Path path = new Path();
        path.addCircle(cx, cy, radius - border, Path.Direction.CCW);
        canvas.clipPath(path, Region.Op.DIFFERENCE);
        float startAngle = -90f;//12点位置
        //内圆环的外接正方形
        RectF arcRect = new RectF(cx - radius, cy - radius, cx + radius, cy + radius);
        for (int i = 0; i < mDataSet.size(); i++) {//绘制扇形圆环
            Entry e = mDataSet.get(i);
            mPaint.setColor(e.color);
            //留1个角度的空白
            canvas.drawArc(arcRect, startAngle, e.angle == 360 ? e.angle : (e.angle - 1), true, mPaint);
            startAngle += e.angle;
        }
        canvas.restore();
        //绘制大圆中心文字数字
        if (!TextUtils.isEmpty(mCenterText)) {
            mPaint.setColor(Color.BLACK);

            mPaint.setTextSize(textSizeBigCount);
            mPaint.setTextAlign(Paint.Align.CENTER);
            canvas.drawText("" + mCount, cx, cy - paddingTotal, mPaint);
            mPaint.setTextSize(textSizeBigCircle2);
            String text = mCenterText;
            //只有一个数据的时候,中间文字为数据类型标签,不是传进来的mCenterText
            if (mDataSet.size() == 1)
                text = mDataSet.get(0).label;
            canvas.drawText(text, cx, cy + textSizeBigCircle2, mPaint);
        }
        // 底部绘制 小圆圈,及label
        if (mDataSet.size() > 2) {
            float scx, scy;
            float sdif = singleItemWidth;

            //最左边到第一个item的距离(留白)
            float sw = (width - mDataSet.size() * sdif) / 2;
            //第一个小圆的圆心
            scy = padding + radius * 2 + paddingBottomCircle + sradius;
            scx = sw + sdif / 2;
            //由于外部限制,这里最多只有6个数据,所以没做多行小圆 的情况处理
            //需求也没有大量数据项的情况,所以其他情况读者自行处理
            for (int i = 0; i < mDataSet.size(); i++) {
                Entry e = mDataSet.get(i);
                mPaint.setColor(e.color);
                canvas.drawCircle(scx, scy, sradius, mPaint);
                mPaint.setColor(Color.WHITE);
                mPaint.setTextSize(textSizeSmallCount);
                canvas.drawText("" + e.value, scx, scy + textSizeSmallCount * 1 / 3, mPaint);
                canvas.save();

                mTextPain.setTextSize(textSizeLabel);
                mTextPain.setTextAlign(Paint.Align.CENTER);
                mTextPain.setColor(0xFF5F5F5F);
                //getWidth()表示绘制多宽后换行
                int end = e.label.length();

                //绘制的文字 色设置固定宽度并自动换行,最多显示两行
                StaticLayout sl = null;
                Class clazz = null;
                try {
                    clazz = Class.forName("android.text.StaticLayout");
                } catch (ClassNotFoundException e1) {
                    e1.printStackTrace();
                }
                Constructor con = null;
                StaticLayout tmp = null;
                try {
                    con = clazz.getConstructor(CharSequence.class, int.class, int.class, TextPaint.class, int.class,
                            Layout.Alignment.class, TextDirectionHeuristic.class, float.class, float.class, boolean.class,
                            TextUtils.TruncateAt.class, int.class, int.class);
                } catch (NoSuchMethodException e1) {
                    e1.printStackTrace();
                }

                try {
                    tmp = (StaticLayout) con.newInstance("" + e.label, 0, end, mTextPain, (int) sdif - 1, Layout.Alignment.ALIGN_NORMAL, TextDirectionHeuristics.FIRSTSTRONG_LTR, 1.0f, 0.0f, true, TextUtils.TruncateAt.MIDDLE, (int) (sdif - 3 * sradius), 2);
                } catch (InstantiationException e1) {
                    e1.printStackTrace();
                } catch (IllegalAccessException e1) {
                    e1.printStackTrace();
                } catch (InvocationTargetException e1) {
                    e1.printStackTrace();
                }
                sl = tmp;

//                StaticLayout sl = new StaticLayout(""+e.label, 0, end, mTextPain, (int)sdif - 1, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true, TextUtils.TruncateAt.MIDDLE, (int)(sdif - 3 * sradius));
                //从0,0开始绘制
                canvas.translate(scx, scy + sradius + paddingTotal);
                sl.draw(canvas);
                canvas.restore();
//                canvas.drawText(""+e.label,scx,scy+sradius*2+paddingTotal,mPaint);
                scx += sdif;
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //这里没有调用父类,因为默认的只是调用了setMeasuredDimension 方法,下面我们自己调用
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int minheight = (int) (padding + radius * 2 + padding);
        if (mDataSet.size() > 2) {
            //加上小面小圆+小圆label文字 + 留白的高度(粗略使用7倍小圆半径),这里是粗略计算,具体自行调节
            minheight += (sradius * 7);
        }

        setMeasuredDimension(
                Math.max(getSuggestedMinimumWidth(),
                        resolveSize(width,
                                widthMeasureSpec)),
                Math.max(getSuggestedMinimumHeight(),
                        resolveSize(minheight,
                                heightMeasureSpec)));
    }

    float convertDpToPixel(float dp) {

        return getResources().getDisplayMetrics().density * dp;

    }

    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;
        }
    }
}

使用

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.philos.piechartdemo.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#15212121"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#FFFFFF"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:drawableLeft="@mipmap/ic_launcher"
                android:drawablePadding="8dp"
                android:gravity="center_vertical"
                android:padding="8dp"
                android:text="单个圆中间显示名字和数量"
                android:textSize="16dp" />

            <com.example.philos.piechartdemo.PieChart
                android:id="@+id/pie_chart"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:background="#FFFFFF"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:drawableLeft="@mipmap/ic_launcher"
                android:drawablePadding="8dp"
                android:gravity="center_vertical"
                android:padding="8dp"
                android:text="两个显示横线和名称"
                android:textSize="16dp" />

            <com.example.philos.piechartdemo.PieChart
                android:id="@+id/pie_chart1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:background="#FFFFFF"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:drawableLeft="@mipmap/ic_launcher"
                android:drawablePadding="8dp"
                android:gravity="center_vertical"
                android:padding="8dp"
                android:text="两个显示横线和名称"
                android:textSize="16dp" />

            <com.example.philos.piechartdemo.PieChart
                android:id="@+id/pie_chart12"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:background="#FFFFFF"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:drawableLeft="@mipmap/ic_launcher"
                android:drawablePadding="8dp"
                android:gravity="center_vertical"
                android:padding="8dp"
                android:text="超过3个显示小圆"
                android:textSize="16dp" />

            <com.example.philos.piechartdemo.PieChart
                android:id="@+id/pie_chart2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:background="#FFFFFF"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:drawableLeft="@mipmap/ic_launcher"
                android:drawablePadding="8dp"
                android:gravity="center_vertical"
                android:padding="8dp"
                android:text="最多6项"
                android:textSize="16dp" />

            <com.example.philos.piechartdemo.PieChart
                android:id="@+id/pie_chart3"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </LinearLayout>
</ScrollView>
public class MainActivity extends AppCompatActivity {

    private PieChart mPieChart;
    private PieChart mPieChart1;
    private PieChart mPieChart12;
    private PieChart mPieChart2;
    private PieChart mPieChart3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mPieChart = (PieChart) findViewById(R.id.pie_chart);
        mPieChart1 = (PieChart) findViewById(R.id.pie_chart1);
        mPieChart12 = (PieChart) findViewById(R.id.pie_chart12);
        mPieChart2 = (PieChart) findViewById(R.id.pie_chart2);
        mPieChart3 = (PieChart) findViewById(R.id.pie_chart3);

        CompanyInfo companyInfo = new CompanyInfo(20, 0, 0, 0, 0, 0);
        ArrayList tmp = createPieChart(companyInfo);
        mPieChart.setData(tmp, "总共有");

        CompanyInfo companyInfo1 = new CompanyInfo(450, 3, 0, 0, 0, 0);
        ArrayList tmp1 = createPieChart(companyInfo1);
        mPieChart1.setData(tmp1, "总共有");

        CompanyInfo companyInfo12 = new CompanyInfo(370, 0, 0, 600, 0, 0);
        ArrayList tmp12 = createPieChart(companyInfo12);
        mPieChart12.setData(tmp12, "总共有");

        CompanyInfo companyInfo2 = new CompanyInfo(0, 0, 0, 50, 200, 10);
        ArrayList tmp2 = createPieChart(companyInfo2);
        mPieChart2.setData(tmp2, "总共有");

        CompanyInfo companyInfo3 = new CompanyInfo(15000, 10, 190, 7, 210, 80);
        ArrayList tmp3 = createPieChart(companyInfo3);
        mPieChart3.setData(tmp3, "总共有");
    }

    private ArrayList createPieChart(CompanyInfo companyInfo) {
        ArrayList tmp = new ArrayList();
        int count = 0;
        if(companyInfo.trademarks_count > 0) {
            count += companyInfo.trademarks_count;
            tmp.add(new PieChart.Entry("商标",companyInfo.trademarks_count));//trademark
        }
        if(companyInfo.domains_count > 0) {
            count += companyInfo.domains_count;
            tmp.add(new PieChart.Entry("域名",companyInfo.domains_count));//trademark
        }
        if(companyInfo.patents_count > 0) {
            count += companyInfo.patents_count;
            tmp.add(new PieChart.Entry("专利",companyInfo.patents_count));//trademark
        }
        if(companyInfo.soft_count > 0) {
            count += companyInfo.soft_count;
            tmp.add(new PieChart.Entry("软件著作权",companyInfo.soft_count));//trademark
        }
        if(companyInfo.original_count > 0) {
            count += companyInfo.original_count;
            tmp.add(new PieChart.Entry("原创著作权公司资质认证",companyInfo.original_count));//trademark
        }
        if(companyInfo.certificate_count > 0) {
            count += companyInfo.certificate_count;
            tmp.add(new PieChart.Entry("公司资质认证",companyInfo.certificate_count));//trademark
        }
        return tmp;
    }

    class CompanyInfo {
        public int trademarks_count;
        public int domains_count;
        public int patents_count;
        public int soft_count;
        public int original_count;
        public int certificate_count;

        public CompanyInfo(int trademarks_count, int domains_count, int patents_count, int soft_count, int original_count, int certificate_count) {
            this.trademarks_count = trademarks_count;
            this.domains_count = domains_count;
            this.patents_count = patents_count;
            this.soft_count = soft_count;
            this.original_count = original_count;
            this.certificate_count = certificate_count;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要在Android中创建自定义圆环,可以使用Canvas和Paint类来绘制。以下是一个简单的例子: 1. 在你的XML布局文件中,添加一个自定义View: ```xml <com.example.myapp.MyCircleView android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 2. 创建一个自定义View类,并覆盖onDraw方法: ```java public class MyCircleView extends View { private Paint paint; private RectF rectF; private float strokeWidth = 20; //圆环宽度 private float progress = 0; //进度 public MyCircleView(Context context, AttributeSet attrs) { super(context, attrs); paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(strokeWidth); paint.setColor(Color.BLUE); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); rectF = new RectF(strokeWidth / 2, strokeWidth / 2, getWidth() - strokeWidth / 2, getHeight() - strokeWidth / 2); canvas.drawArc(rectF, -90, progress, false, paint); } //设置圆环进度 public void setProgress(float progress) { this.progress = progress; invalidate(); } } ``` 3. 在Activity或Fragment中使用自定义View: ```java MyCircleView myCircleView = findViewById(R.id.my_circle_view); myCircleView.setProgress(120); ``` 在此示例中,我们使用了paint对象来绘制圆环。我们还使用RectF类来确定圆环的大小和位置。最后,我们在onDraw方法中使用canvas对象来绘制圆环。我们还添加了setProgress方法,用于设置进度。 希望这可以帮助到你! ### 回答2: Android中可以通过自定义继承自View的类来实现圆环的绘制。首先,在自定义View类的构造方法中初始化画笔,并设置画笔的属性,如颜色、宽度等。然后,在自定义View类的onDraw()方法中调用canvas的drawCircle()方法,传入圆心坐标和半径参数,即可绘制出一个圆。 此外,要实现圆环的效果,可以在drawCircle()方法之前先绘制一个实心圆,再绘制一个较大的同心空心圆。可以通过设置画笔的样式为STROKE,即只画圆边缘的方式,来实现空心圆的效果。 在绘制圆环的过程中,可以利用onMeasure()方法来获取View的宽高,并动态计算圆心坐标和半径,以适应不同的屏幕尺寸。 另外,如果需要显示进度效果,可以通过设置画笔的样式为FILL,并利用drawArc()方法在圆环内部绘制一个扇形,根据进度值设置绘制的角度,来显示进度条。 最后,在使用自定义圆环的时候,可以在XML布局文件中引用该自定义View,并设置相应的属性,如颜色、宽度、进度值等。 ### 回答3: 在Android中,可以通过自定义控件来实现圆环的效果。 首先,我们可以创建一个自定义View类,继承自View类。在自定义类中,我们需要重写onDraw()方法来绘制圆环。 在onDraw()方法中,我们可以通过Canvas类提供的drawArc()方法来绘制圆弧,从而实现圆环的效果。drawArc()方法需要指定圆弧的矩形区域、起始角度、扫过的角度和是否包含中心点。 为了实现一个圆形的圆环效果,我们可以通过计算得出圆弧的矩形区域,起始角度设置为0,扫过的角度设置为360,表示一个完整的圆环。我们还可以设置画笔的宽度、颜色等属性来控制圆环的样式。 在自定义View类中,我们还可以通过重写onMeasure()方法来控制View的大小。在该方法中,我们可以根据需求设置View的宽度和高度,使得圆环显示出来。 最后,在使用自定义圆环的布局文件中,我们可以直接将自定义View类添加到布局中。可以通过设置布局文件中的宽度、高度,以及其他属性设置来调整圆环的样式和位置。 总结起来,Android中可以通过自定义View类和重写onDraw()方法来实现圆环效果。通过计算矩形区域、设置起始角度和扫过的角度,以及设置画笔的属性,可以实现不同样式的圆环效果。最后,在布局中使用该自定义View类来显示圆环

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值