LeafChart-实现自己的小型图表库(2)

来源:互联网 发布:网络推广工资怎么样 编辑:程序博客网 时间:2024/04/28 13:20

一. 设计

  1. 上次写的七日化利率表设计很粗糙,也没有体现面向对象的思想。数据处理都在控件中,而且表格列数比较固定,对于数据和列数不匹配的情况,只能从左向右依次显示数据。另外扩展性很差。
  2. 根据图表的属性,分别抽象出坐标轴、坐标刻度、点、线等类。所以在使用的时候初始化这些数据就可以控制图表的展示。
  3. 抽象出折线统计图和条形统计图公共部分AbsLeafChart,这个类主要处理了控件的有关尺寸、初始化坐标轴和坐标轴刻度、计算每个点在控件的坐标。
  4. 折线统计图LeafLineChart继承AbsLeafChart,只负责画折线图。

二.类介绍

AxisValue 坐标轴刻度

类型 属性 介绍 String label 刻度值 float pointX x 坐标 float pointY y 坐标

Axis 坐标轴

类型 属性 介绍 List values 刻度集合 boolean hasLines 是否连线 boolean isShowText 是否显示坐标轴刻度值 Typeface typeface 刻度字体 int textSize 刻度字体大小 int textColor 刻度字体颜色 int axisColor x/y轴颜色 float axisWidth x/y轴宽度 int axisLineColor 平行于x/y轴的坐标轴颜色 float axisLineWidth 平行于x/y轴的坐标轴宽度 float startX 坐标轴起点x坐标 float startY 坐标轴起点y坐标 float stopX 坐标轴终点x坐标 float stopY 坐标轴终点y坐标

PointValue 点

类型 属性 介绍 float x 占x轴总长度权重 float y 占x轴总长度权重 float diffX 距离原点长度 float diffY 距离原点长度 float originX x坐标 float originY y坐标 String label 标签

ChartData 图表数据

类型 属性 介绍 List values 点集合 boolean hasLabels 是否显示标签 int labelColor 标签背景色 float labelRadius 标签背景弧度

Line extends ChartData 折线

类型 属性 介绍 int lineColor 折线颜色 float lineWidth 折线的宽度 boolean hasPoints 是否画圆点 boolean hasLines 是否画线条 int pointColor 圆点颜色 float pointRadius 圆点半径 boolean isCubic 是否是曲线 boolean isFill 是否填充 int fillColr 填充色

三、实现

暂时只实现了折线图,这里只介绍折线图实现

AbsLeafChart中的相关方法:

尺寸控制

@Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        initViewSize();        resetAsixSize();        resetLineSize();    }

控件宽高和控件内间距初始化

protected void initViewSize() {        mWidth = getMeasuredWidth();        mHeight = getMeasuredHeight();        leftPadding = LeafUtil.dp2px(mContext, 20);        rightPadding = LeafUtil.dp2px(mContext, 10);        topPadding = LeafUtil.dp2px(mContext, 15);        bottomPadding = LeafUtil.dp2px(mContext, 20);    }

设置坐标轴位置

protected void resetAsixSize() {        if(axisX != null){            List<AxisValue> values = axisX.getValues();            int sizeX = values.size(); //几条y轴            float xStep = (mWidth - leftPadding) / sizeX;            axisX.setStepSize(xStep);            for (int i = 0; i < sizeX; i++) {                AxisValue axisValue = values.get(i);                axisValue.setPointY(mHeight);                if(i == 0){                    axisValue.setPointX(leftPadding);                } else {                    axisValue.setPointX(leftPadding + xStep * i);                }            }            axisX.setStartX(0).setStartY(mHeight - bottomPadding)                    .setStopX(mWidth).setStopY(mHeight - bottomPadding);        }        if(axisY != null){            List<AxisValue> values = axisY.getValues();            int sizeY = values.size(); //几条x轴            float yStep = (mHeight - topPadding - bottomPadding) / sizeY;            axisY.setStepSize(yStep);            for (int i = 0; i < sizeY; i++) {                AxisValue axisValue = values.get(i);                axisValue.setPointX(leftPadding);                if(i == 0){                    axisValue.setPointY(mHeight - bottomPadding );                } else {                    axisValue.setPointY(mHeight - bottomPadding - yStep * i);                }            }            axisY.setStartX(leftPadding).setStartY(mHeight - bottomPadding)                    .setStopX(leftPadding).setStopY(0);        }    }

设置点所占比重

protected void resetLineSize() {        if(line != null && axisX != null && axisY != null){            List<PointValue> values = line.getValues();            int size = values.size();            List<AxisValue> axisValuesX = axisX.getValues();            List<AxisValue> axisValuesY = axisY .getValues();            float totalWidth = Math.abs(axisValuesX.get(0).getPointX() - axisValuesX.get(axisValuesX.size() - 1).getPointX());            float totalHeight = Math.abs(axisValuesY.get(0).getPointY() - axisValuesY.get(axisValuesY.size() - 1).getPointY());//            Log.e("=====", "totalWidth:" + totalWidth + ", totalHeight:" + totalHeight);            for (int i = 0; i < size; i++) {                PointValue pointValue = values.get(i);                float diffX = pointValue.getX() * totalWidth;                pointValue.setDiffX(diffX);                float diffY = pointValue.getY() * totalHeight;                pointValue.setDiffY(diffY);//                Log.e("====", "x:" + pointValue.getX() + ", y:" + pointValue.getY());//                Log.e("=======", "diffX:" + pointValue.getDiffX() + ", diffY:" + pointValue.getDiffY());            }        }    }

画坐标轴 刻度值

protected void drawCoordinateText(Canvas canvas) {        if(axisX != null && axisY != null){            //////// X 轴            // 1.刻度            paint.setColor(axisX.getTextColor());            paint.setTextSize(LeafUtil.sp2px(mContext, axisX.getTextSize()));            Paint.FontMetrics fontMetrics = paint.getFontMetrics(); // 获取标题文字的高度(fontMetrics.descent - fontMetrics.ascent)            float textH = fontMetrics.descent - fontMetrics.ascent;            List<AxisValue> valuesX = axisX.getValues();            if(axisX.isShowText()){                for (int i = 0; i < valuesX.size(); i++) {                    AxisValue value = valuesX.get(i);                    float textW = paint.measureText(value.getLabel());                    canvas.drawText(value.getLabel(), value.getPointX() - textW / 2,  value.getPointY() - textH / 2,paint);                }            }////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////            /////// Y 轴            paint.setColor(axisY.getTextColor());            paint.setTextSize(LeafUtil.sp2px(mContext, axisY.getTextSize()));            List<AxisValue> valuesY = axisY.getValues();            if(axisY.isShowText()){                for (AxisValue value : valuesY){                    float textW = paint.measureText(value.getLabel());                    float pointx = value.getPointX() - 1.1f * textW;                    canvas.drawText(value.getLabel(), pointx , value.getPointY(),paint);                }            }        }    }

确定每个点所在位置:根据之前计算的diff计算得到每个点的x、y坐标

protected void setPointsLoc(){        if(line != null){            linePaint.setStrokeWidth(LeafUtil.dp2px(mContext, line.getLineWidth()));            List<PointValue> values = line.getValues();            int size = values.size();            for (int i = 0; i < size; i++) {                PointValue point1 = values.get(i);                float originX1 = point1.getDiffX() + leftPadding;                float originY1 = mHeight - bottomPadding - point1.getDiffY();                point1.setOriginX(originX1).setOriginY(originY1);            }        }    }

画圆点

    protected void drawPoints(Canvas canvas) {        if (line != null) {            if(line.isHasPoints()){                labelPaint.setColor(line.getPointColor());                List<PointValue> values = line.getValues();                for (PointValue point: values) {                    float radius = LeafUtil.dp2px(mContext, line.getPointRadius());                    canvas.drawCircle(point.getOriginX(), point.getOriginY(),                            radius , labelPaint);                }            }        }    }

画每一个点上的标签 : 需要注意的是画标签的时候需要得到标签字符串的高和宽,另外需要控制的是标签边界不要超出控件边界

protected void drawLabels(Canvas canvas) {        if (line != null) {            if(line.isHasLabels()){                labelPaint.setTextSize(LeafUtil.sp2px(mContext, 12));                Paint.FontMetrics fontMetrics = labelPaint.getFontMetrics();                List<PointValue> values = line.getValues();                int size = values.size();                for (int i = 0; i < size; i++) {                    PointValue point = values.get(i);                    String label = point.getLabel();                    Rect bounds = new Rect();                    int length = label.length();                    labelPaint.getTextBounds(label, 0, length, bounds);                    float textW = bounds.width();                    float textH = bounds.height();                    float left, top, right, bottom;                    if(length == 1){                        left = point.getOriginX() - textW * 2.2f;                        right = point.getOriginX() + textW * 2.2f;                    }  else if(length == 2){                        left = point.getOriginX() - textW * 1.0f;                        right = point.getOriginX() + textW * 1.0f;                    } else {                        left = point.getOriginX() - textW * 0.6f;                        right = point.getOriginX() + textW * 0.6f;                    }                    top = point.getOriginY() - 2.5f*textH;                    bottom = point.getOriginY() - 0.5f*textH;//                    if(i > 0){//                        PointValue prePoint = values.get(i - 1);//                        RectF rectF = prePoint.getRectF();//                        if(left <= rectF.right){//                            // 左边与上一个标签重叠//                            top = point.getOriginY() + 1.7f*textH;//                            bottom = point.getOriginY() + 0.5f*textH;//                        }//                    }                    //控制位置                    if(left < 0){                        left = leftPadding;                        right += leftPadding;                    }                    if(top < 0){                        top = topPadding;                        bottom += topPadding;                    }                    if(right > mWidth){                        right -= rightPadding;                        left -= rightPadding;                    }                    RectF rectF = new RectF(left, top, right, bottom);                    float labelRadius = LeafUtil.dp2px(mContext,line.getLabelRadius());                    labelPaint.setColor(line.getLabelColor());                    canvas.drawRoundRect(rectF, labelRadius, labelRadius, labelPaint);                    //drawText                    labelPaint.setColor(Color.WHITE);                    float xCoordinate = left + (right - left - textW) / 2;                    float yCoordinate = bottom - (bottom - top - textH) / 2 ;                    canvas.drawText(point.getLabel(), xCoordinate, yCoordinate, labelPaint);                }            }        }    }

LeafLineChart中的方法:

画折线:记得之前话折线的方法是点与点之间连线。这里的做法是吧每个点用Path记录,最后通过drawPath来画折线

 protected void drawLines(Canvas canvas) {        if(line != null){            linePaint.setColor(line.getLineColor());            linePaint.setStrokeWidth(LeafUtil.dp2px(mContext, line.getLineWidth()));            linePaint.setStyle(Paint.Style.STROKE);            List<PointValue> values = line.getValues();            int size = values.size();            for (int i = 0; i < size; i++) {                PointValue point = values.get(i);                if(i == 0)  path.moveTo(point.getOriginX(), point.getOriginY());                else  path.lineTo(point.getOriginX(), point.getOriginY());            }            canvas.drawPath(path, linePaint);        }    }

画曲线

private void drawCubicPath(Canvas canvas) {        if(line != null){            linePaint.setColor(line.getLineColor());            linePaint.setStrokeWidth(LeafUtil.dp2px(mContext, line.getLineWidth()));            linePaint.setStyle(Paint.Style.STROKE);            float prePreviousPointX = Float.NaN;            float prePreviousPointY = Float.NaN;            float previousPointX = Float.NaN;            float previousPointY = Float.NaN;            float currentPointX = Float.NaN;            float currentPointY = Float.NaN;            float nextPointX = Float.NaN;            float nextPointY = Float.NaN;            List<PointValue> values = line.getValues();            final int lineSize = values.size();            for (int valueIndex = 0; valueIndex < lineSize; ++valueIndex) {                if (Float.isNaN(currentPointX)) {                    PointValue linePoint = (PointValue) values.get(valueIndex);                    currentPointX = linePoint.getOriginX();                    currentPointY = linePoint.getOriginY();                }                if (Float.isNaN(previousPointX)) {                    if (valueIndex > 0) {                        PointValue linePoint = values.get(valueIndex - 1);                        previousPointX = linePoint.getOriginX();                        previousPointY = linePoint.getOriginY();                    } else {                        previousPointX = currentPointX;                        previousPointY = currentPointY;                    }                }                if (Float.isNaN(prePreviousPointX)) {                    if (valueIndex > 1) {                        PointValue linePoint = values.get(valueIndex - 2);                        prePreviousPointX = linePoint.getOriginX();                        prePreviousPointY = linePoint.getOriginY();                    } else {                        prePreviousPointX = previousPointX;                        prePreviousPointY = previousPointY;                    }                }                // nextPoint is always new one or it is equal currentPoint.                if (valueIndex < lineSize - 1) {                    PointValue linePoint = values.get(valueIndex + 1);                    nextPointX = linePoint.getOriginX();                    nextPointY = linePoint.getOriginY();                } else {                    nextPointX = currentPointX;                    nextPointY = currentPointY;                }                if (valueIndex == 0) {                    // Move to start point.                    path.moveTo(currentPointX, currentPointY);                } else {                    // Calculate control points.                    final float firstDiffX = (currentPointX - prePreviousPointX);                    final float firstDiffY = (currentPointY - prePreviousPointY);                    final float secondDiffX = (nextPointX - previousPointX);                    final float secondDiffY = (nextPointY - previousPointY);                    final float firstControlPointX = previousPointX + (LINE_SMOOTHNESS * firstDiffX);                    final float firstControlPointY = previousPointY + (LINE_SMOOTHNESS * firstDiffY);                    final float secondControlPointX = currentPointX - (LINE_SMOOTHNESS * secondDiffX);                    final float secondControlPointY = currentPointY - (LINE_SMOOTHNESS * secondDiffY);                    path.cubicTo(firstControlPointX, firstControlPointY, secondControlPointX, secondControlPointY,                            currentPointX, currentPointY);                }                // Shift values by one back to prevent recalculation of values that have                // been already calculated.                prePreviousPointX = previousPointX;                prePreviousPointY = previousPointY;                previousPointX = currentPointX;                previousPointY = currentPointY;                currentPointX = nextPointX;                currentPointY = nextPointY;            }            canvas.drawPath(path, linePaint);        }    }

用的请看源码。
GitHub:LeafChart

7 0