LeafChart-实现自己的小型图表库(2)
来源:互联网 发布:网络推广工资怎么样 编辑:程序博客网 时间:2024/04/28 13:20
一. 设计
- 上次写的七日化利率表设计很粗糙,也没有体现面向对象的思想。数据处理都在控件中,而且表格列数比较固定,对于数据和列数不匹配的情况,只能从左向右依次显示数据。另外扩展性很差。
- 根据图表的属性,分别抽象出坐标轴、坐标刻度、点、线等类。所以在使用的时候初始化这些数据就可以控制图表的展示。
- 抽象出折线统计图和条形统计图公共部分AbsLeafChart,这个类主要处理了控件的有关尺寸、初始化坐标轴和坐标轴刻度、计算每个点在控件的坐标。
- 折线统计图LeafLineChart继承AbsLeafChart,只负责画折线图。
二.类介绍
AxisValue 坐标轴刻度
Axis 坐标轴
PointValue 点
ChartData 图表数据
Line extends ChartData 折线
三、实现
暂时只实现了折线图,这里只介绍折线图实现
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
- LeafChart-实现自己的小型图表库(2)
- LeafChart-实现自己的小型图表库(1)
- c++ 编写自己的小型游戏开发库(续)
- sChart.js:一个小型简单的图表库
- 小型计算器的实现
- c++ 编写自己的小型游戏开发库
- 如何自己构建一个小型的Zoomeye----从技术细节探讨到实现
- Android 开发:带触控的图表(基金图表的实现)
- 自己写的小型静态服务器
- 自己写的小型嵌入式操作系统
- 小型记事本的VB实现
- 实现小型文件的压缩
- 小型DBMS(c实现)
- Box2D 内存管理 - 小型对象分配器(SOA)的实现
- Box2D 内存管理 - 小型对象分配器(SOA)的实现
- android图表的实现
- 打造自己的专属Linux(一):快速建立一个小型Linux
- 打造自己的专属Linux(一):快速建立一个小型Linux .
- iOS AttributeString 使用详解
- Test2
- 二分
- Cable master<hdoj1551>
- Spring中最小化xml配置一之 四种自动装备方式
- LeafChart-实现自己的小型图表库(2)
- ios开发 导航控制器
- Ajax(1)
- CodeForces 372 A Counting Kangaroos is Fun
- 如何创建并运行Java线程
- JSTL的配置与使用
- 【CodeForces】699C - Vacations(贪心)
- CF 589F 贪心+二分
- java.nio系列二