美文网首页K线图
基于MPAndroidChart实现股票K线图之自定义Y轴标签

基于MPAndroidChart实现股票K线图之自定义Y轴标签

作者: 天才木木 | 来源:发表于2016-06-27 00:32 被阅读2558次

    1 前言

    BarChart用来显示股票成交量。由于股票交易成交量数额巨大,且在滑动查看K线图历史数据时,BarChart的Y轴标签需要动态变化,原生的chart.setAutoScaleMinMaxEnabled(true);只是实现了标签的数值大小适配滑动,但是下图这种更加合理的效果(股/万股/亿股的动态显示)却无法做到。

    ↑请看左下角
    本文就从源码出发,尝试实现这种动态化Y轴标签。

    2 基本思路

    基于MPAndroidChart实现股票K线图之自定义HighLight样式及源码浅析这篇文章提到不同的Renderer类负责绘制整个Chart的不同部分,自然本文的重点也是在负责Y轴绘制逻辑的YAxisRenderer

    整体上看,绘制的过程是:
    1、根据可见的直方图内的最大值和最小值,计算出要在Y轴上显示的label数组(是一个float数组);
    2、确定label的x坐标 ;
    3、根据计算好的位置,绘制label数组里的数字。
    以上对应YAxisRenderer类里的方法是:

    1. computeAxis(float yMin, float yMax);
      根据框架使用者设置的labelCount,从可见范围内的最小到最大值等差地生成一个label 数组(是一个float数组)mEntries
    2. renderAxisLabels(Canvas c);
      根据框架使用者设置的 AxisDependency(LEFT or RIGHT)
      以及 YAxisLabelPosition(OUTSIDE_CHART or INSIDE_CHART)来确定label的x坐标。
    3. drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset);
      通过canvas.drawText( String text, float x, float y, Paint paint)进行绘制。
       protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) {
    
            // draw
            for (int i = 0; i < mYAxis.mEntryCount; i++) {
    //调用mYAxisValueFormatter.getFormattedValue(mEntries[index], this)得到绘制的文本
                String text = mYAxis.getFormattedLabel(i);
    
                if (!mYAxis.isDrawTopYLabelEntryEnabled() && i >= mYAxis.mEntryCount - 1)
                    return;
    
                c.drawText(text, fixedPosition, positions[i * 2 + 1] + offset, mAxisLabelPaint);
            }
        }
    

    以上三个方法由上向下也是代码执行的顺序。而且computeAxis(float yMin, float yMax);计算得到的float数组mEntries,经过mYAxisValueFormatter格式化以后的文本才是被绘制的label文本。
    那么理论上说,只要在mEntries确定之后,drawYLabels()发生之前,动态地去设置YAxisValueFormatter就能够达到文章开头想要的效果。

    3 实践

    3.1 YAxisValueFormatter

    已知成交量直方图Y轴标签的规则是 基于可见范围内的最大值来确定量级:

    1. 成交量<10000,显示 xx股;
    2. 成交量>10000 && 成交量<100000000,显示 xx万股;
    3. 成交量>100000000,显示 xx亿股。
      所以YAxisValueFormatter 的实现如下:
    public class CustomYAxisFormatter implements YAxisValueFormatter {
        private static final int TEN_THOUSAND = 10000;
        private static final int HUNDRED_MILLION = 100000000;
    
        private String unit;
        private int divisor;
    
        public USVolumeYAxisFormatter(float maxVolume) {
            this.unit = getUnit(maxVolume);
            this.divisor = getDivisor(maxVolume);
        }
    
        @Override
        public String getFormattedValue(float value, YAxis yAxis) {
            if (value == 0) {
    //坐标原点,显示单位(股/万股/亿股)
                return unit;
            }
            return String.format("%.2f", value / divisor);
        }
    
        /** 根据最大值的大小来确定单位*/
        public String getUnit(double num) {
            int e = (int) Math.floor(Math.log10(num));
            if (e >= 8) {
                return "亿股";
            } else if (e >= 4) {
                return "万股";
            } else {
                return "股";
            }
        }
    
        /** 根据最大值的大小来确定除数*/
        public int getDivisor(float maxValue) {
            int e = (int) Math.floor(Math.log10(maxValue));
            if (e >= 8) {
                return HUNDRED_MILLION;
            } else if (e >= 4) {
                return TEN_THOUSAND;
            } else {
                return 1;
            }
        }
    }
    

    3.2 YAxisRenderer

    然后按照前面所述,在YAxisRenderer 的computeAxis()完成label数组的计算后,根据最大值来动态设置YAxisValueFormatter

    public class CustomeYAxisRenderer extends YAxisRenderer {
    
        public CustomeYAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer trans) {
            super(viewPortHandler, yAxis, trans);
        }
    
        @Override
        public void computeAxis(float yMin, float yMax) {
            super.computeAxis(yMin, yMax);
            if (mYAxis.mEntryCount > 1) {
                float topLabel = Math.abs(mYAxis.mEntries[mYAxis.mEntryCount - 1]);
                mYAxis.setValueFormatter(new USVolumeYAxisFormatter(topLabel));
            }
        }
    }
    

    3.3 chart.setRendererLeftYAxis

    最后,在BarChart控件初始化的时候,将自定义的YAxisRenderer设置上去:

    CustomeYAxisRenderer yAxisRenderer = new CustomeYAxisRenderer (barChart.getViewPortHandler(), barChart.getAxisLeft(), barChart.getTransformer(YAxis.AxisDependency.LEFT));
    chart.setRendererLeftYAxis(yAxisRenderer);
    

    大功告成!


    效果如上

    相关文章

      网友评论

      • 17d482a88062:demo有吗大佬。
      • 36556fad058d:博主你好,上面k线下面直方图是怎么实现的?两个表格的y轴的数据怎么分离啊
        天才木木:很简单,不要想得太复杂,就两个View
      • a3f4fdbfbb5e:Barchart的y轴怎么能设置固定的值 我设置了YAxis.setValueFormatter不生效,现在y轴的值发现是根据barentry的第二个坐标来变化的
      • ZkJanus:博主 就类似于你图例 左轴那样,谢谢了
      • ZkJanus:怎么设置Y轴数据个数固定 ,数据比较多,可以左右滑动时候更新Y轴数据。不自动
        ZkJanus:@天才木木 博主 算是弄好了吧 !那个Legend,左右滑动值跟着变化,是怎么做的?
        ZkJanus:@天才木木 谢谢你的回复。我代码里是这样写的,有这样设置。可否加一下QQ。我的是QQ:137859626
        天才木木:@ZkJanus 试试 chart.setAutoScaleMinMaxEnabled(true);
      • 01818c973b34:博主,请问如何实现K线图滑动到最左侧的时候,去加载更多数据呢?效果类似于ListView的上拉加载更多数据的效果(只需要文本提示,正在加载更多数据...)
        01818c973b34:这个我知道,我只是想在表中实现那种类似列表的加载更多的效果(UI效果),业务逻辑是可以实现的。不知道这个UI如何在表中实现
        天才木木:@豆瓣影迷 滑动最后一个条目就去请求更多数据吧,具体就是在onChartTranslate的回调里判断chart.getLowestVisibleXIndex() == 0
      • Shawn_GBWang:您好,请教一个问题:怎么设置缩放的范围?比如X轴值是0 4 8 12 16 每次双击屏幕 放大一次 ,我想把值固定在0 1 2 3 4 5 6。
      • d26168ad953a:MACD指标 如何设置颜色中间0的位置固定中间不动,

      本文标题:基于MPAndroidChart实现股票K线图之自定义Y轴标签

      本文链接:https://www.haomeiwen.com/subject/eqlzdttx.html