给Android图表库MPAndroidChart的坐标换行
RaleighKinc
8年前
<p>做统计图时,经常会有这样的需求:x坐标需要换行显示,日和月或者是月和年需要分两行显示。但是找遍MPAndroidChart的各种教程,好像也没有相关的方法可以实现......</p> <p>以LineChart为例,我们想要的效果是类似这样子的:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/2b8194bfcd54f6508cb1e0510653c5ee.png"></p> <p>首先,MPAndroidChart确实是没有提供更改坐标换行显示的方法。在看这篇文章之前,你可能已经尝试过实现一个ValueFormatter,或者是"\r\n"这样的挣扎,发现并没有什么卵用。但就算真给你换行了,如何居中,如何设置两行不同字体也是问题。</p> <p>咋办?看一下源码吧。看看设进去的数据是如何显示出来的。</p> <p>我们是这样来设置坐标轴要显示的内容的:</p> <pre> <code class="language-java">LineData data = new LineData(xValues, dataSets); mChart.setData(data); mChart.invalidate();</code></pre> <p>跟着setData方法进源码,一路经过Chart,BarLineChartBase,最终来到了XAxisRenderer这个类,我们的xValues被赋值给了它的mXAxis。然后,好像就没有然后了......</p> <p>不对,别忘了我们setData之后还调用了invalidate方法请求重绘,重绘会去调用Chart的onDraw方法。再看看继承Chart的BarLineChartBase,它的onDraw方法里出现了这一句:</p> <pre> <code class="language-java">this.mXAxisRenderer.renderGridLines(canvas);</code></pre> <p>感觉离真相越来越近了。我们再跟着这个方法进来,看到里面调用了drawLabels方法,然后drawLabels又调用了drawLabel方法,最终我们来到了这里:</p> <pre> <code class="language-java">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.drawXAxisValue(c, formattedLabel, x, y, mFirstLinePaint, anchor, angleDegrees); }</code></pre> <p>最后进入Utils.drawXAxisValue方法,终于看到了这样的一句:</p> <pre> <code class="language-java">c.drawText(text, drawOffsetX, drawOffsetY, paint);</code></pre> <p>哎呦我去,终于找到了,藏这么深。</p> <p>可以看到坐标轴是用Canvas.drawText显示出来的。了解一下你会发现,通常情况下,drawText方法是不支持"\n\r"的。看来我们只要改drawLabel这个方法就可以了。由于源码几个类的耦合程度比较高,我并没有找到可以通过继承重写的方法实现,不得已只好改源代码了。</p> <p>我修改后的代码是这样的:</p> <pre> <code class="language-java">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); float labelHeight = mXAxis.getTextSize(); float labelInterval = 25f; String[] labels = label.split(" "); Paint mFirstLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mFirstLinePaint.setColor(Color.WHITE); mFirstLinePaint.setTextAlign(Align.CENTER); mFirstLinePaint.setTextSize(Utils.convertDpToPixel(15f)); mFirstLinePaint.setTypeface(mXAxis.getTypeface()); Paint mSecondLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mSecondLinePaint.setColor(0xFF9b9b9b); mSecondLinePaint.setTextAlign(Align.CENTER); mSecondLinePaint.setTextSize(Utils.convertDpToPixel(10f)); mSecondLinePaint.setTypeface(mXAxis.getTypeface()); if (labels.length > 1) { Utils.drawXAxisValue(c, labels[0], x, y, mFirstLinePaint, anchor, angleDegrees); Utils.drawXAxisValue(c, labels[1], x, y + labelHeight + labelInterval, mSecondLinePaint, anchor, angleDegrees); } else { Utils.drawXAxisValue(c, formattedLabel, x, y, mFirstLinePaint, anchor, angleDegrees); } }</code></pre> <p>我在要分行显示的字符串间加了个空格,在这里再用空格切割成两个字符串。我们可以分别给两行设置不同的格式,并且让他们都居中显示。最后绘制的时候,把第二行的y坐标改一下,在第一行的基础上加上一定的高度就可以了。妥妥的。</p> <p>另外,那条黄色的高亮线也是改源码实现的。</p> <p> </p> <p>来自:http://www.jianshu.com/p/e616546c8d21</p> <p> </p>