MPAndroidChart结构和源码分析

使用

在分析之前,先了解如何使用该库。然后根据使用流程开枝散叶般去分析其中的具体分支。首先MPAndroidChart的使用大致可以总结为下面的使用流程图:

MPAndroidChart结构和源码分析_第1张图片
使用流程

下面根据流程图来一步步的粗略介绍下代码中的使用步骤

  • 假设我们从后台接口中成功获取到图表中绘制所需的数据集
  • 然后将数据包装成MPAndroidChart能够识别的数据类型。
  • 最后将数据传给Chart,并通知其刷新绘制

包装过程的代码大致如下:

   //可以通俗的理解一个Entry对应于图表中的一个点
   Entry c1e1 = new Entry(0f, 100000f);    
   Entry c1e2 = new Entry(1f, 140000f);
    ...

   //将Entry转换成DateSet(可以理解为图表中的一条线)
   List valsComp1 = new ArrayList();
   valsComp1.add(c1e1);
   valsComp1.add(c1e2);
   ...
   LineDataSet setComp1 = new LineDataSet(valsComp1, "Company 1")
   
    //将所有的数据集DateSet包装成ChartData  然后set给Chart
    List dataSets = new ArrayList();
    dataSets.add(setComp1);
    dataSets.add(setComp2);
    ...
    LineData data = new LineData(dataSets);
    mLineChart.setData(data);
    mLineChart.invalidate(); // refresh

更多使用方法详解查看官网Wiki

结构分析

如果仔细的去看MPAndroidChart中的每一个图表或者细节的实现是非常枯燥和耗时间的。最好的方法是先选取一个分支了解其大致结构,然后了解细节,最后再了解其他分支。这里根据分析LineChart的源码,大致的总结整理出一张流程图。

MPAndroidChart结构和源码分析_第2张图片
MPAndroidChart结构图.png

根据上面的结构图,可以直观的发现。其实Chart的作用主要是中心调度.具体的实现由其他部分实现,达到各部分解耦的效果。下面我们遵循由点到面的思路去分析整个结构。

1.先看组件部分,该部分主要是包括X、Y轴线,限制线、标注等等。同样的选取AxisBase进行源码分析,其主要是包括轴线宽度、最大最小值、颜色、是否绘制网格线等等,并提供相对于的Set和Get方法。我们也可以根据自己想需求在它的基础上去进一步设置。

2.再看数据部分,大致使用过程在开始就介绍过了。主要就是对数据进行一层一层的装换。同时,其内部提供了相关的增删查减操作。
3.第三部分数据渲染。其主体实现主要是Render及其子类。观察源码可以清楚的看到Render的作用可以总结为两点,一是计算,二是绘制。Chart通过Render先进行图表数据和参数的准备,既计算操作。比如计算出Y轴上的最大最小值,以此可以算出X轴之前的间距。比如如下代码:

/**
     * Sets up the axis values. Computes the desired number of labels between the two given extremes.
     *
     * @return
     */
    protected void computeAxisValues(float min, float max) {

        float yMin = min;
        float yMax = max;

        int labelCount = mAxis.getLabelCount();
        double range = Math.abs(yMax - yMin);

        if (labelCount == 0 || range <= 0 || Double.isInfinite(range)) {
            mAxis.mEntries = new float[]{};
            mAxis.mCenteredEntries = new float[]{};
            mAxis.mEntryCount = 0;
            return;
        }

        // Find out how much spacing (in y value space) between axis values
        double rawInterval = range / labelCount;
        double interval = Utils.roundToNextSignificant(rawInterval);
        ......
        
   }

对于图表是如何控制Render计算和绘制的,这里整理出一张时序图方便理解:

MPAndroidChart结构和源码分析_第3张图片
MPAndroidChart数据渲染时序图.png

大致的解释一下就是,当我们通过Chart.SetData传入数据到图表中,会调用到内部的notifyDataSetChanged()方法,然后会通知Render进行相关的计算操作。值得一提的是,在这个时候会调用Transformer完成"value-touch-offset"的转换过程,其内部是通过操作Matrix实现的。而在图表第一次显示或者后续发生移动、缩放、平移等操作的时候,会调用到Chart.OnDraw方法进行绘制。而在Chart.OnDraw会重新进行计算操作,同时会调用Render的renderXXX方法进行具体的绘制。可以通过观看如下代码更加直观明白:

Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mData == null)
            return;

        long starttime = System.currentTimeMillis();

        // execute all drawing commands
        drawGridBackground(canvas);

        if (mAutoScaleMinMaxEnabled) {
            autoScale();
        }

        if (mAxisLeft.isEnabled())
            mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum, mAxisLeft.isInverted());

        if (mAxisRight.isEnabled())
            mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum, mAxisRight.isInverted());

        if (mXAxis.isEnabled())
            mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false);

        mXAxisRenderer.renderAxisLine(canvas);
        mAxisRendererLeft.renderAxisLine(canvas);
        mAxisRendererRight.renderAxisLine(canvas);

        mXAxisRenderer.renderGridLines(canvas);
        mAxisRendererLeft.renderGridLines(canvas);
        mAxisRendererRight.renderGridLines(canvas);

        if (mXAxis.isEnabled() && mXAxis.isDrawLimitLinesBehindDataEnabled())
            mXAxisRenderer.renderLimitLines(canvas);

        if (mAxisLeft.isEnabled() && mAxisLeft.isDrawLimitLinesBehindDataEnabled())
            mAxisRendererLeft.renderLimitLines(canvas);

        if (mAxisRight.isEnabled() && mAxisRight.isDrawLimitLinesBehindDataEnabled())
            mAxisRendererRight.renderLimitLines(canvas);

        // make sure the data cannot be drawn outside the content-rect
        int clipRestoreCount = canvas.save();
        canvas.clipRect(mViewPortHandler.getContentRect());

        mRenderer.drawData(canvas);

        // if highlighting is enabled
        if (valuesToHighlight())
            mRenderer.drawHighlighted(canvas, mIndicesToHighlight);

        // Removes clipping rectangle
        canvas.restoreToCount(clipRestoreCount);

        mRenderer.drawExtras(canvas);

        if (mXAxis.isEnabled() && !mXAxis.isDrawLimitLinesBehindDataEnabled())
            mXAxisRenderer.renderLimitLines(canvas);

        if (mAxisLeft.isEnabled() && !mAxisLeft.isDrawLimitLinesBehindDataEnabled())
            mAxisRendererLeft.renderLimitLines(canvas);

        if (mAxisRight.isEnabled() && !mAxisRight.isDrawLimitLinesBehindDataEnabled())
            mAxisRendererRight.renderLimitLines(canvas);

        mXAxisRenderer.renderAxisLabels(canvas);
        mAxisRendererLeft.renderAxisLabels(canvas);
        mAxisRendererRight.renderAxisLabels(canvas);

        if (isClipValuesToContentEnabled()) {
            clipRestoreCount = canvas.save();
            canvas.clipRect(mViewPortHandler.getContentRect());

            mRenderer.drawValues(canvas);

            canvas.restoreToCount(clipRestoreCount);
        } else {
            mRenderer.drawValues(canvas);
        }

        mLegendRenderer.renderLegend(canvas);

        drawDescription(canvas);

        drawMarkers(canvas);

        if (mLogEnabled) {
            long drawtime = (System.currentTimeMillis() - starttime);
            totalTime += drawtime;
            drawCycles += 1;
            long average = totalTime / drawCycles;
            Log.i(LOG_TAG, "Drawtime: " + drawtime + " ms, average: " + average + " ms, cycles: "
                    + drawCycles);
        }
    }

4.结构图中将ViewPortHandler单独放出的原因是因为它在Chart和Render之间担任一个“信使”的作用(这里的理解不知道是否有误),Chart将一些数据和数据告诉ViewPortHandler,比如最大最小缩放,Chart的长度和宽度,偏移量等等,而Render则从其中获取这部分信息。对于MPAndroidChart中的其他部分,没做过多的分析,比如数据格式器、动画等需进一步了解分析。

你可能感兴趣的:(MPAndroidChart结构和源码分析)