一步一步教你写股票走势图——分时图二(自定义xy轴)


目录
一步一步教你写股票走势图——分时图一(概述)
一步一步教你写股票走势图——分时图二(自定义xy轴)
一步一步教你写股票走势图——分时图三(对齐图表、自定义柱状图高亮)
一步一步教你写股票走势图——分时图四(高亮联动)
一步一步教你写股票走势图——分时图五(自定义标记)


demo更新地址https://github.com/AndroidJiang/StockChart


今天要研究的是分时图的自定义xy轴,为什么要自定义xy轴呢?因为我们原生的mp库满足不了我们分时图的需求,所以就得改啦!


X轴定义

正常的分时图x轴是这样的效果


一步一步教你写股票走势图——分时图二(自定义xy轴)_第1张图片
这里写图片描述

如果用原生mp,效果如下


一步一步教你写股票走势图——分时图二(自定义xy轴)_第2张图片
这里写图片描述

看到差别了吧,怎么才能让中间显示11:30/13:00呢?

  • 重写XAxis
    将要显示的labels封装进去,方便set和get,不封装也行,这样好看点。
public class MyXAxis extends XAxis {
    private SparseArray labels;
    public SparseArray getXLabels() {
        return labels;
    }
    public void setXLabels(SparseArray labels) {
        this.labels = labels;
    }
}

- 重写XAxisRenderer

这是什么?这里面都是渲染x轴,关于x轴的绘制,都在这个类里面,包括labels,lines以及添加的limitline(如果看到这,觉得不适应的话,建议多看看mp的源码,笔者也是从看源码开始的,看不懂多看看,慢慢理解,这些是程序员必须得走的路)。可以根据库自带的类去改。比如:

XAxisRendererBarChart.class

@Override
    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
        };

        BarData bd = mChart.getData();
        int step = bd.getDataSetCount();

        for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) {

            position[0] = i * step + i * bd.getGroupSpace()
                    + bd.getGroupSpace() / 2f;

            // consider groups (center label for each group)
            if (step > 1) {
                position[0] += ((float) step - 1f) / 2f;
            }

            mTrans.pointValuesToPixel(position);

            if (mViewPortHandler.isInBoundsX(position[0]) && i >= 0
                    && i < mXAxis.getValues().size()) {

                String label = mXAxis.getValues().get(i);

                if (mXAxis.isAvoidFirstLastClippingEnabled()) {

                    // avoid clipping of the last
                    if (i == mXAxis.getValues().size() - 1) {
                        float width = Utils.calcTextWidth(mAxisLabelPaint, label);

                        if (position[0] + width / 2.f > mViewPortHandler.contentRight())
                            position[0] = mViewPortHandler.contentRight() - (width / 2.f);

                        // avoid clipping of the first
                    } else if (i == 0) {

                        float width = Utils.calcTextWidth(mAxisLabelPaint, label);

                        if (position[0] - width / 2.f < mViewPortHandler.contentLeft())
                            position[0] = mViewPortHandler.contentLeft() + (width / 2.f);
                    }
                }

                drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees);
            }
        }
    }

我们自己重写后:

 @Override
    protected void drawLabels(Canvas c, float pos, PointF anchor) {
        float[] position = new float[]{
                0f, 0f
        };
        int count = mXAxis.getXLabels().size();
        for (int i = 0; i < count; i++) {
            /*获取label对应key值,也就是x轴坐标0,60,121,182,242*/
            int ix = mXAxis.getXLabels().keyAt(i);
            position[0] = ix;
            /*在图表中的x轴转为像素,方便绘制text*/
            mTrans.pointValuesToPixel(position);
            String label = mXAxis.getXLabels().valueAt(i);
            c.drawText(label, position[0],
                    pos + 10,
                    mAxisLabelPaint);
        }
    }

代码不做解释,请看注释。


一步一步教你写股票走势图——分时图二(自定义xy轴)_第3张图片
这里写图片描述

ok,效果差不多实现,不过发现x轴左右两个数据越界了,我们可以继续优化,修改如下:

  /*x轴越界*/
            if (mViewPortHandler.isInBoundsX(position[0])) {
                String label = mXAxis.getXLabels().valueAt(i);
                /*文本长度*/
                int labelWidth = Utils.calcTextWidth(mAxisLabelPaint, label);
                /*右出界*/
                if ((labelWidth / 2 + position[0]) > mChart.getViewPortHandler().contentRight()) {
                    position[0] = mChart.getViewPortHandler().contentRight() - labelWidth / 2;
                } else if ((position[0] - labelWidth / 2) < mChart.getViewPortHandler().contentLeft()) {//左出界
                    position[0] = mChart.getViewPortHandler().contentLeft() + labelWidth / 2;
                }
                c.drawText(label, position[0],
                        pos+10,
                        mAxisLabelPaint);
            }
一步一步教你写股票走势图——分时图二(自定义xy轴)_第4张图片
这里写图片描述

ok,x轴坐标部分定义基本完成!关于x轴线的绘制,原理和坐标一样,首先参考XAxisRendererBarChart的绘制:

 @Override
    public void renderGridLines(Canvas c) {

        if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled())
            return;

        float[] position = new float[] {
                0f, 0f
        };

        mGridPaint.setColor(mXAxis.getGridColor());
        mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth());

        BarData bd = mChart.getData();
        int step = bd.getDataSetCount();

        for (int i = mMinX; i < mMaxX; i += mXAxis.mAxisLabelModulus) {

            position[0] = i * step + i * bd.getGroupSpace() - 0.5f;

            mTrans.pointValuesToPixel(position);

            if (mViewPortHandler.isInBoundsX(position[0])) {

                c.drawLine(position[0], mViewPortHandler.offsetTop(), position[0],
                        mViewPortHandler.contentBottom(), mGridPaint);
            }
        }
    }

我们里面造一个出来:

 /*x轴垂直线*/
    @Override
    public void renderGridLines(Canvas c) {
        if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled())
            return;
        float[] position = new float[]{
                0f, 0f
        };
        mGridPaint.setColor(mXAxis.getGridColor());
        mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth());
        mGridPaint.setPathEffect(mXAxis.getGridDashPathEffect());
        int count = mXAxis.getXLabels().size();
        if (!mChart.isScaleXEnabled()) {
            count -= 1;
        }
        for (int i = 0; i < count; i ++) {
            int ix = mXAxis.getXLabels().keyAt(i);
            position[0] = ix;
            mTrans.pointValuesToPixel(position);
            c.drawLine(position[0], mViewPortHandler.offsetTop(), position[0],
                    mViewPortHandler.contentBottom(), mGridPaint);
        }

    }

注:一定要知道库自带的mViewPortHandler,它能告诉你大多数你想知道的东西,有哪些,请自行查看源码。


- 重写LineChart
重写的目的是为了将某些参数传入到自定义的类中,也就是我们刚刚定义的两个类,我们里面的那些参数都是通过自定义LineChart穿传进去的,如果不重写,也是可以的,只不过activity的代码会更多而已,不便于查看。

  mXAxis = new MyXAxis();
  mXAxisRenderer = new MyXAxisRenderer(mViewPortHandler, (MyXAxis) mXAxis, mLeftAxisTransformer,this);

关于X轴自定义部分已经结束,如有问题,请留言或者邮件我。github上原作者项目有人提问能不能在x轴放图片,如果上面的代码理解了,图片当然不成问题。下面我们搞一搞Y轴。



Y轴定义

y轴有什么要搞的啊,我们来看一下分时图的成交量图,也就是柱状图。


一步一步教你写股票走势图——分时图二(自定义xy轴)_第5张图片
这里写图片描述

我靠,这跟原生库差十万八千里啊,说像的话,也行,可以设置一个只显示最大和最小, setShowOnlyMinMax(true);那么y轴我们的效果图就是这个样子的

一步一步教你写股票走势图——分时图二(自定义xy轴)_第6张图片
这里写图片描述

样子是像了点,但是跟需求还是不一样啊。好了,我们还是乖乖自定义吧。步骤和x轴一模一样,先来个 YAxis

public class MyYAxis extends YAxis {
    private String minValue;
    public MyYAxis() {
        super();
    }
    public MyYAxis(AxisDependency axis) {
        super(axis);
    }
    public void setShowMaxAndUnit(String minValue) {
        setShowOnlyMinMax(true);
        this.minValue = minValue;
    }
    public String getMinValue(){
        return minValue;
    }
}

很简单,我们仍是稍微封装了几个方法。下面我们来定义Renderer,我们同样找系统现成的类,YAxisRendererRadarChart,里面重写了computeAxisValues方法,代码较多,这里不贴出来了,我们研究下,发现参数传入了最大最小值,我们找到这样一段代码是有用的

  if (mYAxis.isShowOnlyMinMaxEnabled()) {

                mYAxis.mEntryCount = 2;
                mYAxis.mEntries = new float[2];
                mYAxis.mEntries[0] = yMin;
                mYAxis.mEntries[1] = yMax;

            }

刚好符合我们的需求,这个类就是相当于提供y轴的数据。
我们重写也是类似的

 @Override
    protected void computeAxisValues(float min, float max) {
        /*只显示最大最小情况下*/
        if (mYAxis.isShowOnlyMinMaxEnabled()) {
            mYAxis.mEntryCount = 2;
            mYAxis.mEntries = new float[2];
            mYAxis.mEntries[0] = min;
            mYAxis.mEntries[1] = max;
            return;
        }
  }

数据有了,我们就来画y轴labels。

 @Override
    protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) {
        /*当有最小text的时候*/
        if (!TextUtils.isEmpty(mYAxis.getMinValue()) && mYAxis.isShowOnlyMinMaxEnabled()) {
            for (int i = 0; i < mYAxis.mEntryCount; i++) {
             /*获取对应位置的值*/
                String text = mYAxis.getFormattedLabel(i);
                if (i == 0) {
                    text = mYAxis.getMinValue();
                }
                if (i == 1) {
                    c.drawText(text, fixedPosition, mViewPortHandler.contentTop() + offset * 2.5f + 3, mAxisLabelPaint);
                } else if (i == 0) {
                    c.drawText(text, fixedPosition, mViewPortHandler.contentBottom() - 3, mAxisLabelPaint);
                }
            }
        }
    }    

代码真的很简单,完全高仿库自带的。
源码:

  protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) {

        // draw
        for (int i = 0; i < mYAxis.mEntryCount; i++) {

            String text = mYAxis.getFormattedLabel(i);

            if (!mYAxis.isDrawTopYLabelEntryEnabled() && i >= mYAxis.mEntryCount - 1)
                return;

            c.drawText(text, fixedPosition, positions[i * 2 + 1] + offset, mAxisLabelPaint);
        }
    }

如果发现位置显示的不好,可自行调整。

最后是重写BarChart,这里不写了,和LineChart一样,可以查看demo源码,下面贴出效果图:


一步一步教你写股票走势图——分时图二(自定义xy轴)_第7张图片
这里写图片描述


ok,本篇关于xy轴的自定义就讲到这里,如果有疑问,欢迎留言,笔者将尽自己所能,帮助小伙伴们!下一篇将讲解折线图和柱状图的对齐以及柱状图的自定义高亮(因为库本来的高亮不满足需求嘛)。


目录
一步一步教你写股票走势图——分时图一(概述)
一步一步教你写股票走势图——分时图二(自定义xy轴)
一步一步教你写股票走势图——分时图三(对齐图表、自定义柱状图高亮)
一步一步教你写股票走势图——分时图四(高亮联动)
一步一步教你写股票走势图——分时图五(自定义标记)


demo更新地址https://github.com/AndroidJiang/StockChart


你可能感兴趣的:(一步一步教你写股票走势图——分时图二(自定义xy轴))