给Android图表库MPAndroidChart的坐标换行

RaleighKinc 7年前
   <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>