Android 自定义View饼状图(带有折线并显示数据)

效果图:

1.attrs:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="XPieView">
        <attr name="pie_color0" format="color"/>
        <attr name="pie_color1" format="color"/>
        <attr name="pie_color2" format="color"/>
        <attr name="pie_color3" format="color"/>
        <attr name="pie_color4" format="color"/>
        <attr name="pie_progress0" format="float"/>
        <attr name="pie_progress1" format="float"/>
        <attr name="pie_progress2" format="float"/>
        <attr name="pie_progress3" format="float"/>
        <attr name="pie_progress4" format="float"/>
<!--        pie_width_isPieWidth:-->
<!--        是否确定android:layout_width决定饼状图+文字数据的宽度: -->
<!--        false:layout_width决定饼状图的大小,文字数据不算在里面-->
<!--        true:layout_width决定饼状图+文字数据的大小-->
        <attr name="pie_layoutWidth_isPieWidth" format="boolean"/>
<!--        是否进行大小排序-->
        <attr name="pie_isRank" format="boolean"/>
    </declare-styleable>

</resources>

2.JAVA代码:

/**
 * created by: Eroch
 * time: 2020/3/15
 * introduce: 自定义饼状图,xml最多可同时显示5个数据,如需直接java代码加数据,可自行修改代码
 * 求圆形任意一点坐标:
 *                 圆心坐标(x0,y0)
 *                 半径:R
 *                 角度a0
 *                 圆上任意一点(x1,y1)
 *                 则:
 *                 x1 =  x0 + R * cos(a0 * π / 180 )
 *                 y1 = y0 + R + sin(a0 * π / 180 )
 */
public class XPieView extends View {
    private final static int LINE_LENGTH = 80;//直线线的长度
    private final static int CORNER_LENGTH = 10;//折线的长度
    private final static int TEXT_MARGIN = 5;//线的长度
    private final static int TEXT_SIZE = 10;
    //isPieWidth:android:layout_width决定的饼状图的大小还是饼状图+饼状图文字数据的大小
    //true:layout_width决定饼状图+文字数据的大小
    //false:layout_width决定饼状图的大小,文字数据不算在里面
    private boolean isPieWidth;
    private float[] progressS;//所有数据
    private int[] colorS;//所有颜色
    private Paint paint;//画笔
    private float percent;//百分比
    private float centreX,centreY;//饼状图的中心点(centreX,centreY)
    private float r;//半径

    public XPieView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        paint.setDither(true);//抗抖动
        paint.setAntiAlias(true);//抗锯齿
        paint.setStrokeWidth(2);//画笔粗细
        paint.setStyle(Paint.Style.FILL);//画笔样式为填充
        paint.setStrokeCap(Paint.Cap.ROUND);//设置笔端样式为圆
        paint.setTextSize(TEXT_SIZE);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.XPieView);
        if (array != null){
            int color0 = array.getInt(R.styleable.XPieView_pie_color0, 0);
            int color1 = array.getInt(R.styleable.XPieView_pie_color1, 0);
            int color2 = array.getInt(R.styleable.XPieView_pie_color2, 0);
            int color3 = array.getInt(R.styleable.XPieView_pie_color3, 0);
            int color4 = array.getInt(R.styleable.XPieView_pie_color4, 0);
            colorS = new int[]{color0, color1, color2, color3, color4};
            float progress0 = array.getFloat(R.styleable.XPieView_pie_progress0, 0);
            float progress1 = array.getFloat(R.styleable.XPieView_pie_progress1, 0);
            float progress2 = array.getFloat(R.styleable.XPieView_pie_progress2, 0);
            float progress3 = array.getFloat(R.styleable.XPieView_pie_progress3, 0);
            float progress4 = array.getFloat(R.styleable.XPieView_pie_progress4, 0);
            percent =360 / (progress0 + progress1 + progress2 + progress3 + progress4);
            progressS = new float[]{progress0,progress1,progress2,progress3,progress4};
            isPieWidth = array.getBoolean(R.styleable.XPieView_pie_layoutWidth_isPieWidth, false);
            boolean isRank = array.getBoolean(R.styleable.XPieView_pie_isRank, false);//是否进行排序
            if (isRank){
                //冒泡(最大的在在前面)
                for (int i = 0; i < progressS.length; i++) {
                    for (int j = 0; j < progressS.length - 1 - i; j++) {
                        if (progressS[j] < progressS[j + 1]){
                            float num = progressS[j+1];
                            int color = colorS[j + 1];
                            progressS[j + 1] = progressS[j];
                            colorS[j + 1] = colorS[j];
                            progressS[j] = num;
                            colorS[j] = color;
                        }
                    }
                }
            }
        }
        array.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //wrap_content:-2
        //match_parent:-1
        //android:layout_width="150dp":150*2
        //android:layout_width="70dp":70*2
        int width = getLayoutParams().width;
        int height = getLayoutParams().height;
        if (width >0 || height > 0 ){
            int index = width > height ? width : height;
            if (isPieWidth){
                //width = 饼+文字数据宽度
                centreX = centreY = index - (LINE_LENGTH*2)>>1;
                r = index>>2;
                setMeasuredDimension(index,index);
            }else {
                //width = 饼的宽度
                centreX = centreY = (index>>1) + LINE_LENGTH;
                r = index>>2;
                setMeasuredDimension(index + LINE_LENGTH*2,index + LINE_LENGTH*2);
            }
        }else {
            centreX = centreY = 200;
            r = 100;
            setMeasuredDimension(400,400);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint.setColor(colorS[0]);
        //计算最大文字的宽度
        float textLength = (String.valueOf(progressS[0]).length() - 1 )/ 1.5f * TEXT_SIZE;
        //为保证饼状图的美观,所以这统一X坐标
        float referenceRightX = centreX + r + LINE_LENGTH - textLength;
        float referenceLeftX = centreX - r - LINE_LENGTH + textLength;
        float p = 0;
        for (int i = 0; i < progressS.length; i++) {
            if (progressS[i] == 0){
                return;
            }
            if (i != 0) {
                p += progressS[i - 1] * percent;
            }
            paint.setColor(colorS[i]);
            canvas.drawArc(centreX-r,centreX-r,centreX+r,centreX+r,i == 0 ? 0 : p, progressS[i]*percent,true,paint);
            float data = p + (percent * progressS[i])/2;
            double a = Math.toRadians(data);//把数字90 转换成 90度
            double sin = Math.sin(a);
            double cos = Math.cos(a);
            //饼的任意一点坐标
            float v = (float) (centreX + r * cos);
            float v1 = (float) (centreY + r * sin);
            //折线坐标
            float cornerX = (float) (centreX  +  (r+CORNER_LENGTH) * cos);
            float cornerY = (float) (centreX  + (r+CORNER_LENGTH) * sin);
            canvas.drawLine(v,v1,cornerX,cornerY,paint);

            //270°和90°分别是12点钟和6点钟,作为区别左右边的凭据
            if (data <270 && data > 90){
                //在左边
                canvas.drawLine( cornerX,cornerY,referenceLeftX,cornerY,paint);
                canvas.drawText(String.valueOf(progressS[i]),referenceLeftX -TEXT_MARGIN - textLength,cornerY+(TEXT_SIZE>>2),paint);
            }else {
                //在右边
                canvas.drawLine(cornerX ,cornerY,referenceRightX,cornerY,paint);
                canvas.drawText(String.valueOf(progressS[i]),referenceRightX + TEXT_MARGIN,cornerY+(TEXT_SIZE>>2),paint);
            }
        }

    }

}

3.Xml里使用:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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:paddingLeft="10dp"
    android:paddingTop="10dp"
    tools:context=".MainActivity">

    <com.test.XPieView
        android:background="@color/colorPrimaryDark"
        app:pie_color0="#FF4888"
        app:pie_color1="#FFCA28"
        app:pie_color2="#37C0FF"
        app:pie_color3="#FF4F1F"
        app:pie_color4="#86EF8B"
        app:pie_progress0="10"
        app:pie_progress1="70"
        app:pie_progress2="100"
        app:pie_progress3="20"
        app:pie_progress4="10"
        android:layout_centerInParent="true"
        android:layout_marginTop="100dp"
        android:layout_marginLeft="100dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</RelativeLayout>

End...

这几天没任务,看了一下自定义View,哪里写法还可以改进的,欢迎留言!

猜你喜欢

转载自blog.csdn.net/qq_41873558/article/details/104899880
今日推荐