Android 扇形统计图

作者: Android师哥 | 来源:发表于2017-12-26 17:21 被阅读64次
    night_rain.png

    Android 开发中不可避免的会有一些统计的需求,为了应对千变万化的需求,作为我们开发者就需要学会利用Android暴露给我们的接口,来绘制我们自己的View;

    首先继承View

        //用户数据
        private List<Integer> mNumbers = new ArrayList<>();
        //用户指定的颜色
        private List<Integer> mColors = new ArrayList<>();
        //用户指定的提示内容
        private List<String> hintContents = new ArrayList<>();
        //用户指定文字的颜色
        private int textColor = Color.parseColor("#000000");
        //用户指定线条的颜色
        private int lineColor = Color.parseColor("#000000");
        //用户数据的总数
        private int sum = 0;
        //控件的高度
        private int mMeasuredHeight;
        //控件的宽度
        private int mMeasuredWidth;
        //中心点坐标
        private float cirX;
        private float cirY;
    
    public class StatisticsCircle extends View {
    
        public StatisticsCircle(Context context) {
            super(context);
        }
    
        public StatisticsCircle(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initPaint();
        }
    
        public StatisticsCircle(Context context, AttributeSet attrs) {
            super(context, attrs);
            initPaint();
        }
    }
    

    初始化画笔

    既然要绘制自己的view,画笔是必不可少的

        /**
         * 初始化画笔
         */
        private void initPaint() {
            initPicPaint();
            initLinePaint();
            initTextPaint();
        }
    
        /**
         * 绘制文字画笔
         */
        private void initTextPaint() {
            mTextPaint = new Paint();
            //设置抗锯齿
            mTextPaint.setAntiAlias(true);
            //图像抖动处理
            mTextPaint.setDither(true);
            //填充和描边
            mTextPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            //设置线和文字的颜色
            mTextPaint.setColor(textColor);
            //设置文字的大小
            mTextPaint.setTextSize(CommonUtil.getConverUtil().converSp2Px(10));
        }
    
        /**
         * 绘制直线画笔
         */
        private void initLinePaint() {
            mLinPaint = new Paint();
            mLinPaint.setAntiAlias(true);
            mLinPaint.setDither(true);
            mLinPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            //设置画笔的宽度
            mLinPaint.setStrokeWidth(CommonUtil.getConverUtil().convertDp2Px(1));
            //设置画笔的颜色
            mLinPaint.setColor(lineColor);
        }
    
        /**
         * 绘制弧形的画笔
         */
        private void initPicPaint() {
            mPicPaint = new Paint();
            //抗锯齿
            mPicPaint.setAntiAlias(true);
            //抖动处理
            mPicPaint.setDither(true);
            //填充和描边
            mPicPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        }
    

    绘制逻辑

    画笔准备好了,就要在Android给我们的画布上进行绘制了


    绘制弧形
    • 首先我们要明确目标既然是扇形统计图,一定是一个一个的弧形;
    • 绘制弧形需要先确定一个矩形区域来固定弧形的位置;
    • 绘制弧形还需要一个弧形的角度,这个角度就要根据数据来计算了;
    • 弧形的位置、角度都有了,就可以开始绘制了;

    绘制第一段直线
    • 绘制直线首先要确定起始点和结束点X轴Y轴的坐标;
    • 这里重要的一点就是角度和弧度是不同的,需要转换,例如(Math.sin((drawAngle - angle / 2) * Math.PI / 180) * (radius + (mMeasuredHeight / 8)));
    • 计算好坐标的位置,直接画线;

    绘制第二段直线
    • 第二段直线的绘制方式就比较简单了,可以根据自己的实际情况,绘制,起始点就是第一点直线的终点,结束点全靠自己发挥;

    绘制小圆点
    • 这个也比较简单了,小圆点的绘制需要圆心的坐标和一个半径。(当然,画笔就不能少的,不然拿什么绘制);
    • 需要注意的是一定要在第二点直线结束的位置把小圆点的半径加进去,不然和你预想的直线长度就会有微小的偏差;
        private void drawHorizontal(Canvas canvas) {
            //绘制圆形的区域
            RectF mRectF = new RectF();
            //判断文字的高度
            Rect mJudgeRect = ijudgePaintTextHeight(mTextPaint, "判断");
            //文字的高度
            int mJudgeHeight = mJudgeRect.height();
            /*
            *获取圆的直径
            *控件的高-文字的高(给上下写字预留空间)-划线的最高值(划线最长为高度的1/8)
            */
            float diameter = mMeasuredHeight - mJudgeHeight * 2 - mMeasuredHeight / 4 - CommonUtil.getConverUtil().convertDp2Px(10);
            //获取圆的半径
            float radius = diameter / 2;
            //设置圆形的区域
            mRectF.set(cirX - radius,
                    (mMeasuredHeight - diameter) / 2,
                    diameter + (cirX - radius),
                    (mMeasuredHeight - diameter) / 2 + diameter);
            //第一次绘制角度
            float startAngle = 270;
            //绘制过的角度
            float drawAngle = 0;
            for (int i = 0; i < mNumbers.size(); i++) {
                //获取第一个数据
                int number = mNumbers.get(i);
                //计算需要绘制的角度
                float angle = ((float) number / sum) * 360;
                //设置每次绘制时画笔的颜色
                mPicPaint.setColor(mColors.get(i));
                //开始绘制扇形
                canvas.drawArc(mRectF, startAngle, angle, true, mPicPaint);
                //计算下一次开始绘制扇形的角度
                startAngle = (startAngle + angle) >= 360 ? ((startAngle + angle) - (float) 360) : (startAngle + angle);
                //计算绘制过的角度
                drawAngle += angle;
                //计算绘制第一段直线开始的坐标X
                float startX = (float) (Math.sin((drawAngle - angle / 2) * Math.PI / 180) * (radius - mMeasuredHeight / 8) + cirX);
                //计算绘制第一段直线开始的坐标Y
                float startY = cirY - (float) (Math.cos((drawAngle - angle / 2) * Math.PI / 180) * (radius - mMeasuredHeight / 8));
                //计算绘制第一段直线结束的坐标X
                float middleX = (float) (Math.sin((drawAngle - angle / 2) * Math.PI / 180) * (radius + (mMeasuredHeight / 8)) + cirX);
                //计算绘制第一段直线结束的坐标Y
                float middleY = cirY - (float) (Math.cos((drawAngle - angle / 2) * Math.PI / 180) * (radius + (mMeasuredHeight / 8)));
                //计算第二段直线绘制结束的坐标X
                float endX = 0;
                //结束位置小圆点开始X轴坐标
                float startMinCircularX = 0;
                //通过判断结束X轴的坐标相对圆形位置来确定坐标
                float startTextX = 0;
                //文本内容
                String content = hintContents.get(i) + "(" + CommonUtil.getConverUtil().converPercent(sum, number) + ")";
                Rect mRect = ijudgePaintTextHeight(mTextPaint, content);
                if (middleX > cirX) {
                    endX = cirX + radius + mMeasuredHeight / 8;
                    startMinCircularX = endX + CommonUtil.getConverUtil().convertDp2Px(2);
                    startTextX = startMinCircularX + CommonUtil.getConverUtil().convertDp2Px(5);
                } else if (middleX < cirX) {
                    endX = cirX - radius - mMeasuredHeight / 8;
                    startMinCircularX = endX - CommonUtil.getConverUtil().convertDp2Px(2);
                    startTextX = startMinCircularX - mRect.width() - CommonUtil.getConverUtil().convertDp2Px(5);
                } else {
                    endX = cirX - radius - mMeasuredHeight / 8;
                    startMinCircularX = endX - CommonUtil.getConverUtil().convertDp2Px(2);
                    startTextX = startMinCircularX - CommonUtil.getConverUtil().convertDp2Px(5);
                }
                //计算第二段直线绘制结束的坐标Y
                float endY = middleY;
                //绘制第一段直线
                canvas.drawLine(startX, startY, middleX, middleY, mLinPaint);
                //绘制第二段直线
                canvas.drawLine(middleX, middleY, endX, endY, mLinPaint);
                //绘制黑色小圆点
                canvas.drawCircle(startMinCircularX, endY, CommonUtil.getConverUtil().convertDp2Px(2), mLinPaint);
                //绘制文字
                canvas.drawText(content, startTextX, endY + mJudgeRect.height() / 2, mTextPaint);
            }
        }
    

    效果图

    扇形统计图简单效果.png

    学习自绘控件做的一个Demo,大概的实现了效果,不足之处还请提出,共同进步!

    相关文章

      网友评论

        本文标题:Android 扇形统计图

        本文链接:https://www.haomeiwen.com/subject/mtsjgxtx.html