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

效果图:

Android 自定义View饼状图(带有折线并显示数据)_第1张图片

 

1.attrs:




    
        
        
        
        
        
        
        
        
        
        




        

        
    

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里使用:




    

 

End...

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

你可能感兴趣的:(Android 自定义View饼状图(带有折线并显示数据))