1. 简介
前两篇简单介绍了这个自定义图标库。
LeafChart-实现自己的小型图表库(1)
LeafChart-实现自己的小型图表库(2)
之前LeafChart只支持曲线图,现在在之前的基础上,添加了直方图。
1.1 效果图
1.2 设置
- 直方图宽度
- 边框宽度、颜色
- 是否填充
- 是否有标签
- 标签背景色、弧度
2. 实现
2.1 类介绍
为了更简便的控制直方图的属性,定义直方图类Square,该类同样继承自ChartData。
Square
类型 | 属性 | 介绍 |
---|---|---|
int | width | 直方图宽度 |
int | borderWidth | 边界宽度 |
int | borderColor | 边界颜色 |
boolean | isFill | 是否填充 |
2.2 新增属性
AbsLeafChart是曲线图和直方图的抽象类,主要负责坐标轴的绘制、尺寸的测量、点位置设置等。
在实现曲线图的时候,默认第一条平行于y轴的坐标轴从0点开始,对于曲线图,绘制效果影响不大,但是对于直方图来说,由于每个直方图有自己的宽度,如果从0点开始,则第一个直方图会到0点左边去。
所以,新增属性startMarginX,第一个点距y轴距离。
<declare-styleable name="AbsLeafChart">
<attr name="startMarginX" format="dimension"/>
<attr name="startMarginY" format="dimension"/>
</declare-styleable>
2.3 绘制方块
- 在每一个点出绘制一个方块
private void drawSquares(Canvas canvas) {
if (square != null) {
//1.画直方图边界
linePaint.setColor(square.getBorderColor());
if(!square.isFill()){
linePaint.setStrokeWidth(LeafUtil.dp2px(getContext(), square.getBorderWidth()));
linePaint.setStyle(Paint.Style.STROKE);
}
List<PointValue> values = square.getValues();
float width = LeafUtil.dp2px(getContext(), square.getWidth());
for (PointValue point : values) {
RectF rectF = new RectF(point.getOriginX() - width / 2,
point.getOriginY(), point.getOriginX() + width / 2, axisX.getStartY());
canvas.drawRect(rectF, linePaint);
}
}
}
2.4 绘制标签
标签绘制和曲线图一样,所以直接调用父类的标签绘制方法。
if (square != null && square.isHasLabels()) {
super.drawLabels(canvas, square);
}
3 使用
使用步骤和曲线图基本一致。
private void initSquareChart() {
Axis axisX = new Axis(getAxisValuesX());
axisX.setAxisColor(Color.parseColor("#FF4081")).setTextColor(Color.DKGRAY).setHasLines(false);
Axis axisY = new Axis(getAxisValuesY());
axisY.setAxisColor(Color.parseColor("#FF4081")).setTextColor(Color.DKGRAY).setHasLines(false).setShowText(true);
leafSquareChart.setAxisX(axisX);
leafSquareChart.setAxisY(axisY);
leafSquareChart.setChartData(getSquares());
}
4.新增原点处坐标轴控制
4.1 效果图
4.2 自定义属性
<attr name="coordinateMode">
<enum name="across" value="1"/>
<enum name="intersect" value="2"/>
<enum name="x_across" value="3"/>
<enum name="y_across" value="4"/>
</attr>
private static class Mode{
/**
* 交叉,x、y轴都超出0点
*/
public static final int ACROSS = 1;
/**
* 相交, x、y轴交于0点
*/
public static final int INTERSECT = 2;
/**
* x轴超出0点
*/
public static final int X_ACROSS = 3;
/**
* y轴超出0点
*/
public static final int Y_ACROSS = 4;
}
4.3 位置处理
- x轴
private void resetAsixX() {
if(axisX != null){
List<AxisValue> values = axisX.getValues();
int sizeX = values.size(); //几条y轴
float xStep = (mWidth - leftPadding - startMarginX) / sizeX;
for (int i = 0; i < sizeX; i++) {
AxisValue axisValue = values.get(i);
axisValue.setPointY(mHeight);
if(i == 0){
axisValue.setPointX(leftPadding + startMarginX);
} else {
axisValue.setPointX(leftPadding + startMarginX + xStep * i);
}
}
switch (coordinateMode){
case Mode.ACROSS:
case Mode.X_ACROSS:
axisX.setStartX(leftPadding * 0.5f);
break;
case Mode.INTERSECT:
case Mode.Y_ACROSS:
axisX.setStartX(leftPadding);
break;
}
axisX.setStartY(mHeight - bottomPadding).setStopX(mWidth).setStopY(mHeight - bottomPadding);
}
}
- y轴
private void resetAsixY() {
if(axisY != null){
List<AxisValue> values = axisY.getValues();
int sizeY = values.size(); //几条x轴
float yStep = (mHeight - topPadding - bottomPadding - startMarginY) / sizeY;
for (int i = 0; i < sizeY; i++) {
AxisValue axisValue = values.get(i);
axisValue.setPointX(leftPadding);
if(i == 0){
axisValue.setPointY(mHeight - bottomPadding - startMarginY);
} else {
axisValue.setPointY(mHeight - bottomPadding - startMarginY - yStep * i);
}
}
switch (coordinateMode){
case Mode.ACROSS:
case Mode.Y_ACROSS:
axisY.setStartY(mHeight - bottomPadding * 0.5f);
break;
case Mode.INTERSECT:
case Mode.X_ACROSS:
axisY.setStartY(mHeight - bottomPadding);
break;
}
axisY.setStartX(leftPadding).setStopX(leftPadding).setStopY(0);
}
}