使用MPAndroidChart实现K线图(2)——自定义XY轴

目录

使用MPAndroidChart实现K线图(1)——基本用法

使用MPAndroidChart实现K线图(2)——自定义XY轴

使用MPAndroidChart实现K线图(3)——自定义柱状图

使用MPAndroidChart实现K线图(4)——图表联动、加载更多

使用MPAndroidChart实现K线图(5)——高亮联动、横竖屏切换


MPAndroidChart中的X轴和Y轴的标签内容,可以调用setValueFormatter(IAxisValueFormatter f)方法,对不同的value返回不同的显示内容,如下所示:

        xac.setValueFormatter(new IAxisValueFormatter() {
            @Override
            public String getFormattedValue(float value, AxisBase axis) {
                int v = (int) value;
                if (!xValues.containsKey(v) && xValues.containsKey(v - 1)) {
                    v = v - 1;
                }
                String x = xValues.get(v);
                return TextUtils.isEmpty(x) ? "" : x;
            }
        });

(说明:不想显示的标签一定要返回空字符串,不要返回null,因为MPAndroidChart的后续代码会调用返回值的一些方法,而null会出现空指针异常。)

但是标签的位置以及颜色等信息,都是轴的渲染器AxisRenderer控制的,特别是标签位置,在源码中是固定的,想要改变只能自定义渲染器。

 

重写X轴渲染器XAxisRenderer

X轴的标签,我们想要的效果是:第一个标签右移显示完整,与边缘有一定间隔,最后一个标签左移显示完整,与边缘有一定间隔,其他标签不变。由XAxisRenderer的源码可知,标签的绘制在drawLabels(Canvas c, float pos, MPPointF anchor)方法中,且第一个标签已经向右平移。接下来要做的就是,自定义渲染器InBoundXAxisRenderer,继承XAxisRenderer,重写drawLabels()方法。

    protected int interval;

    @Override
    protected void drawLabels(Canvas c, float pos, MPPointF anchor) {
        final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
        boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled();

        float[] positions = new float[mXAxis.mEntryCount * 2];
        for (int i = 0; i < positions.length; i += 2) {
            // only fill x values
            if (centeringEnabled) {
                positions[i] = mXAxis.mCenteredEntries[i / 2];
            } else {
                positions[i] = mXAxis.mEntries[i / 2];
            }
        }
        mTrans.pointValuesToPixel(positions);

        for (int i = 0; i < positions.length; i += 2) {
            float x = positions[i];
            if (mViewPortHandler.isInBoundsX(x)) {
                String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis);
                if (mXAxis.isAvoidFirstLastClippingEnabled()) {
                    // avoid clipping of the last
                    float width = Utils.calcTextWidth(mAxisLabelPaint, label);
                    if (i == mXAxis.mEntryCount * 2 - 2 && mXAxis.mEntryCount > 1) {
                        x -= width / 2 + interval;
                        // avoid clipping of the first
                    } else if (i == 0) {
                        x += width / 2 + interval;
                    }
                }

                drawLabel(c, label, x, pos, anchor, labelRotationAngleDegrees);
            }
        }
    }

只修改了两处:一处是,第一个标签i=0时,对x加一个间隔interval;另一处是,最后一个标签i为最后一个时,x向左平移一半以及一个间隔interval。

接着把这个渲染器设置给CombinedChart(因BarChart已取消X轴显示,所以不需设置), 其中的间隔interval,在创建时传入。

        Transformer trans = cc.getTransformer(YAxis.AxisDependency.LEFT);
        //自定义X轴标签位置
        cc.setXAxisRenderer(new InBoundXAxisRenderer(cc.getViewPortHandler(), cc.getXAxis(), trans, 10));

重写Y轴渲染器YAxisRenderer

Y轴的标签,我们想要的效果是:最上方的一个标签,平移到当前y值的下方,其余标签平移到当前y值的上方。由YAxisRenderer的源码可知,标签的绘制在drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset)方法中,且传入了偏移量offset,在renderAxisLabels(Canvas c)方法中调用了这个方法,其中:

        float yoffset = Utils.calcTextHeight(mAxisLabelPaint, "A") / 2.5f + mYAxis.getYOffset();

可见,标签的Y值多加了1/2.5(即0.4)个文字高度。因此,自定义渲染器InBoundYAxisRenderer,继承YAxisRenderer,重写drawYLabels()方法,在此基础上继续平移即可。

    @Override
    protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) {
        final int from = mYAxis.isDrawBottomYLabelEntryEnabled() ? 0 : 1;
        final int to = mYAxis.isDrawTopYLabelEntryEnabled() ? mYAxis.mEntryCount : (mYAxis.mEntryCount - 1);

        // draw
        int labelHeight = Utils.calcTextHeight(mAxisLabelPaint, "A");
        for (int i = from; i < to; i++) {
            String text = mYAxis.getFormattedLabel(i);
            float os = i == mYAxis.mEntryCount - 1 ? -0.9F * labelHeight : 0.7F * labelHeight;
            c.drawText(text, fixedPosition, positions[i * 2 + 1] + offset - os, mAxisLabelPaint);
        }
    }

只修改了一处,最后一个标签继续向下平移0.9个文字高度(预留部分间隙),其余标签继续向上平移0.7个文字高度。

接着把这个渲染器设置给CombinedChart和BarChart。

        Transformer trans = cc.getTransformer(YAxis.AxisDependency.LEFT);
        //自定义Y轴标签位置
        cc.setRendererLeftYAxis(new InBoundYAxisRenderer(cc.getViewPortHandler(), cc.getAxisLeft(), trans));

        //自定义Y轴标签位置
        bc.setRendererLeftYAxis(new InBoundYAxisRenderer(bc.getViewPortHandler(), bc.getAxisLeft(),
                bc.getTransformer(YAxis.AxisDependency.LEFT)));

运行程序,绘制效果图如下:

使用MPAndroidChart实现K线图(2)——自定义XY轴_第1张图片

X轴和Y轴的标签位置已达到预期。

关于Render的理解:

渲染器Render是用来控制绘制的类,用来控制标签(Label)、网格线(GridLine)、轴线(ZeroLine)、限制线(LimitLine)等的位置和颜色,以及其他的绘制功能;图表用来控制数据(Data)、值(Value)、高亮(Highlighted)、附加信息(Extra)等的绘制。其中,轴的标签内容虽然也可以在Render中控制,但不建议这样做,因为已经提供了自定义标签内容的方法setValueFormatter(),不建议在自定义类中重复已提供的功能。后续图表的一些功能,都是在自定义Render情况下实现的。

下一步要做的是:

自定义BarChart的Render,使Bar的颜色与红涨绿跌对应,且绘制柱状图时向左平移0.5个单位,与上部分的K线对齐。

 

完整demo地址:https://github.com/ShallowBillow/KlineDemo

你可能感兴趣的:(MPAndroidChart)