个人认为比helloCharts强大很多.,不要问为啥.
一开始以为, 设置margin或padding,让图表距离边界有段距离就能显示出来, 但是尝试之后发现不可行.
MPAndroidChart提供了一个让X坐标轴的开始结束Label避免被裁切的方法xAxis.setAvoidFirstLastClipping(true),但是使用这个方法会让坐标轴显示的很难看. 老外的库对中文的处理还是差了一些.
我在翻看了MPAndroidChart的Issues时, 发现有人和我遇到了类似的问题
The last vertical line is not showing in the Line Chart #1723
经过我仔细思索, 我发觉最右侧的一根垂线不就是右侧的Y轴吗?
如果我设置了右侧的Y轴, 将右侧Y轴的文字设置成透明色, 那么最后侧的垂线不就显示出来了吗? 然后再给右侧Y轴设置偏移setXOffset()即可让图表显示在正确的位置了.
YAxis rightAxis = lineChart.getAxisRight();
rightAxis.setDrawAxisLine(true);
rightAxis.setDrawGridLines(false);
rightAxis.setTextColor(Color.TRANSPARENT);
rightAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART);
rightAxis.setXOffset(15f);
可以看到X轴的最后一个坐标值为红色.
我开始的想法是在最后一个坐标值的位置上盖一个蒙版之类的东西, 坐标显示的时候就能利用图像混合模式之类的技术让其看起来是红色的. 后来发现这样的思路实现起来基本不现实.
于是开始找MPAndroidChart的源码, 看看它是如何绘制X轴的坐标值的.
首先X轴有一个设置坐标值颜色的方法:
xAxis.setTextColor(Color.BLACK);
com.github.mikephil.charting.components.ComponentBase#setTextColor
/**
* the text color to use for the labels
*/
protected int mTextColor = Color.BLACK;
public void setTextColor(int color) {
mTextColor = color;
}
只是给mTextColor成员变量赋值. 这个类中并没有使用到这个值, 但是注意到这个mTextColor的修饰符是protected, 可以向导, 肯定是某个子类使用到了该值. 但是我直接这样找没有找到关键位置.
于是我直接从LineChart类入手
public class LineChart extends BarLineChartBase<LineData> implements LineDataProvider
protected XAxisRenderer mXAxisRenderer;
public class XAxisRenderer extends AxisRenderer {
protected XAxis mXAxis; //X轴
public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans) {
super(viewPortHandler, trans);
this.mXAxis = xAxis;
mAxisLabelPaint.setColor(Color.BLACK); //设置绘制Label的画笔默认颜色为黑色
mAxisLabelPaint.setTextAlign(Align.CENTER);
mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f));
}
//.......
}
mAxisLabelPaint.setTypeface(mXAxis.getTypeface());
mAxisLabelPaint.setTextSize(mXAxis.getTextSize());
mAxisLabelPaint.setColor(mXAxis.getTextColor()); //改变画笔颜色
仔细观察可以看到继承关系 XAxis –> AxisBase –> ComponentBase , 这个ComponentBase 就是我们最开始找的那个设置颜色方法的类. 一切似乎都清晰起来.
既然是使用画笔画的Text, 那么肯定会有canvas.drawText().继续向下找.
protected void drawLabel(Canvas c, String label, int xIndex, float x, float y, PointF anchor,float angleDegrees) {
String formattedLabel = mXAxis.getValueFormatter().getXValue(label, xIndex,mViewPortHandler);
Utils.drawText(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees);//这里使用画笔画Text
}
protected void drawLabel(Canvas c, String label, int xIndex, Paint otherPaint, float x, float y, PointF anchor, float angleDegrees) {
String formattedLabel = mXAxis.getValueFormatter().getXValue(label, xIndex, mViewPortHandler);
Utils.drawText(c, formattedLabel, x, y, otherPaint, anchor, angleDegrees); //这里我使用传入的画笔绘制Text
}
这样, 我只要在某种情况下将画笔的颜色改为红色, 我就能实现目的了. 接下来找到哪里调用drawLabel这个方法就成功了一半了.
可以找到方法调用关系 renderAxisLabels –> drawLabels –> drawLabel.
public void renderAxisLabels(Canvas c) {
if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled())
return;
float yoffset = mXAxis.getYOffset();
mAxisLabelPaint.setTypeface(mXAxis.getTypeface());
mAxisLabelPaint.setTextSize(mXAxis.getTextSize());
mAxisLabelPaint.setColor(mXAxis.getTextColor());
if (mXAxis.getPosition() == XAxisPosition.TOP) { //坐标轴在顶部
drawLabels(c, mViewPortHandler.contentTop() - yoffset,new PointF(0.5f, 1.0f));
//....省略部分代码
} else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { //坐标轴在底部
drawLabels(c, mViewPortHandler.contentBottom() + yoffset,new PointF(0.5f, 0.0f));
}
//....省略部分代码
}
/**
* draws the x-labels on the specified y-position
*/
protected void drawLabels(Canvas c, float pos, PointF anchor) {
final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
// pre allocate to save performance (dont allocate in loop)
float[] position = new float[]{
0f, 0f
};
for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) {
position[0] = i;
mTrans.pointValuesToPixel(position);
if (mViewPortHandler.isInBoundsX(position[0])) {
String label = mXAxis.getValues().get(i);
if (mXAxis.isAvoidFirstLastClippingEnabled()) {
// avoid clipping of the last
if (i == mXAxis.getValues().size() - 1 && mXAxis.getValues().size() > 1) {
float width = Utils.calcTextWidth(mAxisLabelPaint, label);
if (width > mViewPortHandler.offsetRight() * 2
&& position[0] + width > mViewPortHandler.getChartWidth())
position[0] -= width / 2;
// avoid clipping of the first
} else if (i == 0) {
float width = Utils.calcTextWidth(mAxisLabelPaint, label);
position[0] += width / 2;
}
}
drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees);
}
}
}
protected void drawLabels(Canvas c, float pos, PointF anchor, boolean isNeedRed) {
final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
// pre allocate to save performance (dont allocate in loop)
float[] position = new float[]{
0f, 0f
};
for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) {
//....省略部分代码
if (i == mMaxX && isNeedRed) { //如果是最后一个Label,并且是需要设置为红色
mAxisLabelPaint.setColor(Color.RED); //将画笔设置为红色
//调用我自己重载的drawLabel, 传入红色的画笔绘制
drawLabel(c, label, i, mAxisLabelPaint, position[0], pos, anchor, labelRotationAngleDegrees);
} else {
drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees);
}
}
}
}
这样的话, 我折线图LineChart最后一个X轴坐标的Label都变成红色的了. 显然这样是不行的. 因为我项目中其他地方也用到了折线图.
那么我注意到, 需要变为红色的Label只有是当我Label个数为7(也就是一周,索引为0~6)的情况. 其他时候都还要使用默认颜色. 庆幸的是我项目中只有一个地方Label个数为7, 要不然还真不好改. 根据调用关系我们找到renderAxisLabels().
我的思路: 根据我们项目中的实际情况,让renderAxisLabels()调用我重载的drawLabels()方法即可. 然后再根据情况让drawLabels()调用我重载的drawLabel()即可.
那么我就需要修改renderAxisLabels()方法了.
//.....省略部分代码
else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) {
//当X轴位于底部,并且Label个数为7(索引0~6)时,我调用我重载的使用红色画笔绘制的方法
if (mMaxX == Calendar.DAY_OF_WEEK - 1) {
drawLabels(c, mViewPortHandler.contentBottom() + yoffset,
new PointF(0.5f, 0.0f), true); //我重载的需要红色画笔的方法
} else {
drawLabels(c, mViewPortHandler.contentBottom() + yoffset,
new PointF(0.5f, 0.0f));
}
}
//.....省略部分代码
最后, 感谢MPAndroidChart的作者PhilJay, 让我们能如此简单的绘制出漂亮的图表, 我看到您一直在努力的维护着这个库. 再次感谢您的付出!