background

Financial software is now a powerful K-chart, supporting a variety of indicators. But calculating and plotting exponentials is actually a little bit more complicated. I don’t trade stocks or currencies, so I don’t understand the market implications of indicators and I’m not very interested in them. Not long ago, I made a related demand. Today, I will summarize it.

MPAndroidChart

Github.com/PhilJay/MPA… Here’s the Github address. The version I used was V2.2.5, with many customizations. MPAndroidChart is very powerful, and users can customize it for their special needs, because the library design is flexible and performance is good. There is a paid version, but I think the open source version can meet developers’ needs in terms of performance and functionality. Now I would like to take a look at its structural design, and then talk about its use with practical examples.

The design of the MPAndroidChart

According to the picture above, briefly describe the functions of each package:

  • Animation, animation

  • The buffer data class, used to improve rendering efficiency, can be thought of as a cache. For example, we want to draw a bar graph where each data point is converted into a bar (four points, one point for each corner of a rectangular bar),BarBufferWhat does a class do? According to theEntryThe value (point) of the data is converted into a rectangular image.
. start forifloat 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;
      elsebottom *= 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;
    }
Copy the code
  • Chart – The package contains various chart classes
  • Components – Other components of the diagram, such as description/axis/bounding line/Legend, etc
  • Data — Raw data class, andbufferDifferent, this package encapsulates different data types according to different charts.
  • Formatter — The format of the text to draw (for example, the format of the scale value on the x axis)
  • Highlight — highlight (the highlighted state that appears when a point on a chart is selected)
  • Interfaces – Global interface definitions (mainly data-related interface definitions) in a project
  • Jobs — The slide and zoom work of chart
  • Listener – Various listeners
  • Render – Renderer, all drawing work (various chart drawing, axis drawing, background splitter, legend/highlight, etc.)
  • Util — Utility classes, the most important of which areViewPortHandler.Transformer

Uml class diagram structure

There are a lot of diagrams supported by the project, so it is more complicated to present all the diagrams in UML. Let’s take LineChart(line diagram) as an example. Try to understand the design of the library with as little information as possible, so we only include graphs, not x/Y/Legend/MarkView, etc.

chart

Chart package is full of graph-related classes. Here, LineChart is taken as an example to analyze its inheritance hierarchy and the responsibilities of each parent class. The first is chart.class, the most basic base class.

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

Chart inherits from ViewGroup and overrides onMeasure and onLayout methods. Some abstract template methods are also defined. Chart’s main responsibility is to carry out measure and layout, as well as some public behavior definition, and other related work is assigned to corresponding objects. For example, rendering goes to #mRender, gesture listener goes to #mChartTouchListener, zoom/move goes to #mViewPortHandler. BarLineChartBase inherits from Chart and, as the class name suggests, abstracts some of the common behavior of line and bar graphs. LineChart inherits from BarLineChartBase and initializes #mRender

render

Renderer, render responsibility. LineChartRenderer implements the drawData method as follows:

	@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);
    }
Copy the code

The drawDataSet method doesn’t bother.

Transformer

The data values are mapped to pixels on the screen, which is achieved by matrix. The mapping of PATH mainly uses the following two methods.

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);
}
Copy the code

Signal processing

The figure above is the sequence diagram of gesture move operation, and then modify matrix. ViewPortHandler updates the view according to matrix, and Chart redraws it. Here is the code to modify 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);
    }
Copy the code

animation

The realization of animation is realized by using attribute animation. Taking line graph as an example, the value of attribute animation ranges from 0 to 1. Each drawing cycle calculates the range of line graph to be drawn according to the value of attribute animation, so as to achieve animation effect. Taking the animation shown in the GIF image during package analysis as an example, the sequence diagram is as follows:

The optimization of MPAndroidChart

This library performance optimization is convenient for later repair.

Above from this library each aspect has analyzed its design, the realization principle. The next article will show you how to implement different metrics based on the library. Some indicators are purely graphs and can only be calculated numerically, while others are presented in a more specific way and need to be customized to the library.