美文网首页
MPandroidchart源码查看

MPandroidchart源码查看

作者: 成虫_62d0 | 来源:发表于2021-03-30 19:37 被阅读0次

背景

金融软件现在的k线图功能强大,支持各种各样的指标。但是指数的计算和绘制其实是有点复杂的。我不炒股也不炒币,所以对指标背后蕴含的市场含义不懂,也不太感兴趣。前不久做了一个相关的需求,今天总结一下。

MPAndroidChart

https://github.com/PhilJay/MPAndroidChart 这是github地址。笔者使用的版本为v2.2.5,做了很多定制。 MPAndroidChart功能很强大,使用者一些特殊的需求也可以自己定制实现,因为该库的设计很灵活,性能也不错。其有付费版本,但我觉得基于开源版本就可以在性能和功能上满足开发者的需求。下面我想先捋一下它的结构设计,然后结合实际例子讲一下它的使用。

MPAndroidChart的设计

image

根据上图简明扼要叙述一下各包的作用:

  • animation -- 动画


    image
  • buffer 数据类,用来提高绘制效率,可以看成是缓存。 举个例子,我们要绘制柱状图,其中每个数据从点转换成柱状(四个点,矩形柱状每个角对应一个点),BarBuffer类是怎么做的呢?根据Entry数据的value(点)转成矩形图像。

... start fori
      float left = x - barWidth + barSpaceHalf;
      float right = x + barWidth - barSpaceHalf;
      float bottom, top;
      if (mInverted) {
          bottom = y >= 0 ? y : 0;
          top = y <= 0 ? y : 0;
      } else {
          top = y >= 0 ? y : 0;
          bottom = y <= 0 ? y : 0;
      }

      // multiply the height of the rect with the phase
      if (top > 0)
          top *= phaseY;
      else
          bottom *= phaseY;

    addBar(left, top, right, bottom);
              
... end fori      


protected void addBar(float left, float top, float right, float bottom) {

        if (index >= buffer.length - 1) {
            return;
        }
        buffer[index++] = left;
        buffer[index++] = top;
        buffer[index++] = right;
        buffer[index++] = bottom;
    }
  • chart -- 包里面包含各种图表类
  • components -- 图表的其它组件,例如描述/轴/限制线/legend 等等
  • data -- 原始数据类,与buffer有所不同,这个包里根据不同图表封装了不同的数据类型。
  • formatter -- 要绘制的文字的格式(例如x轴的刻度值的格式)
  • highlight -- 高亮线(选中图表上某个点时出现的高亮状态)
  • interfaces -- 项目中全局的接口定义(主要是数据相关的的接口定义)
  • jobs -- chart的滑动缩放处理工作
  • listener -- 各种监听器
  • render -- 渲染器, 所有的绘制工作(各种图表的绘制,轴的绘制,背景分割线,legend/highlight等等)
  • util -- 工具类,最重要的有ViewPortHandler, Transformer

uml类图结构

项目支持的图表很多,uml全部呈现显得比较繁杂,我们就以LineChart(线图)为例。尽量用最少的信息来理解该库的设计,所以我们只包含线图,不包含x轴/y轴/legend/markview等。
[图片上传失败...(image-eb4cc9-1617104235932)]

chart

chart包里面都是图表相关的类,这里以LineChart为例,剖析它的继承层次,以及每一个父类的职责。
首先是Chart.class这个最基础的基类。

public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Entry>>> extends
        ViewGroup
        implements ChartInterface {
        
        ...
}

Chart继承自ViewGroup,其重写了onMeasure以及onLayout方法。还定义了一些抽象的模版方法。Chart的主要职责就是进行measure和layout,以及一些公共行为定义,其它相关工作交给了对应的对象。例如绘制交给了#mRender,手势监听交给了#mChartTouchListener,缩放/移动处理交给了#mViewPortHandler
BarLineChartBase继承自Chart,从类名就可看出它抽象了线图和柱状图的一些公共行为。
LineChart继承自BarLineChartBase,初始化#mRender

render

渲染器,绘制职责。LineChartRenderer实现的drawData方法如下:

    @Override
    public synchronized void drawData(Canvas c) {

        int width = (int) mViewPortHandler.getChartWidth();
        int height = (int) mViewPortHandler.getChartHeight();

        if (mDrawBitmap == null
                || (mDrawBitmap.get().getWidth() != width)
                || (mDrawBitmap.get().getHeight() != height)) {

            if (width > 0 && height > 0) {

                mDrawBitmap = new WeakReference<>(Bitmap.createBitmap(width, height, mBitmapConfig));
                if (mDrawBitmap.get() == null) {
                    return;
                }
                mBitmapCanvas = new Canvas(mDrawBitmap.get());
            } else
                return;
        }

        mDrawBitmap.get().eraseColor(Color.TRANSPARENT);

        LineData lineData = mChart.getLineData();

        for (ILineDataSet set : lineData.getDataSets()) {

            if (set.isVisible() && set.getEntryCount() > 0)
                drawDataSet(c, set);
        }

        c.drawBitmap(mDrawBitmap.get(), 0, 0, mRenderPaint);
    }

drawDataSet方法就不深究了。

Transformer

把数据值映射成屏幕上的像素点,用matrix实现, path的映射主要用到下面俩个方法。

public void prepareMatrixValuePx(float xChartMin, float deltaX, float deltaY, float yChartMin) {

        float scaleX = (float) (mViewPortHandler.contentWidth() / deltaX);
        float scaleY = (float) (mViewPortHandler.contentHeight() / deltaY);

        if (Float.isInfinite(scaleX)) {
            scaleX = 0;
        }
        if (Float.isInfinite(scaleY)) {
            scaleY = 0;
        }

        // setup all matrices
        mMatrixValueToPx.reset();
        mMatrixValueToPx.postTranslate(-xChartMin, -yChartMin);
        mMatrixValueToPx.postScale(scaleX, -scaleY);
    }
    
public void pathValueToPixel(Path path) {

    path.transform(mMatrixValueToPx);
    path.transform(mViewPortHandler.getMatrixTouch());
    path.transform(mMatrixOffset);
}

手势处理

image

上图是手势move操作的时序图,然后修改matrix, ViewPortHandler根据matrix更新视图,Chart再重新绘制;
下面是修改matrix的代码:

private void performDrag(MotionEvent event) {

        mLastGesture = ChartGesture.DRAG;

        mMatrix.set(mSavedMatrix);

        OnChartGestureListener l = mChart.getOnChartGestureListener();

        float dX, dY;

        // check if axis is inverted
        if (mChart.isAnyAxisInverted() && mClosestDataSetToTouch != null
                && mChart.getAxis(mClosestDataSetToTouch.getAxisDependency()).isInverted()) {

            // if there is an inverted horizontalbarchart
            if (mChart instanceof HorizontalBarChart) {
                dX = -(event.getX() - mTouchStartPoint.x);
                dY = event.getY() - mTouchStartPoint.y;
            } else {
                dX = event.getX() - mTouchStartPoint.x;
                dY = -(event.getY() - mTouchStartPoint.y);
            }
        } else {
            dX = event.getX() - mTouchStartPoint.x;
            dY = event.getY() - mTouchStartPoint.y;
        }

        mMatrix.postTranslate(dX, dY);

        if (l != null)
            l.onChartTranslate(event, dX, dY);
    }

动画

动画的实现是使用属性动画进行实现的,以线图为例,属性动画的值从0到1,每个绘制周期根据属性动画的值计算要绘制的线图范围,这样就实现了动画效果。以包分析时的gif图片所展示的动画为例,时序图如下:


image

MPAndroidChart的优化

这个库性能优化方便后续再补。

上面从这个库的各个方面分析了它的设计,实现原理。下一篇会介绍基于该库实现不同的指标,有的指标只是纯粹的线图,只做数值计算即可,有的指标的展现形式有点特殊,就需要对这个库进行定制化。

相关文章

网友评论

      本文标题:MPandroidchart源码查看

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