MPandroidchart源碼查看

背景

金融軟件現(xiàn)在的k線圖功能強大,支持各種各樣的指標(biāo)。但是指數(shù)的計算和繪制其實是有點復(fù)雜的。我不炒股也不炒幣,所以對指標(biāo)背后蘊含的市場含義不懂,也不太感興趣。前不久做了一個相關(guān)的需求,今天總結(jié)一下。

MPAndroidChart

https://github.com/PhilJay/MPAndroidChart 這是github地址。筆者使用的版本為v2.2.5,做了很多定制。 MPAndroidChart功能很強大,使用者一些特殊的需求也可以自己定制實現(xiàn),因為該庫的設(shè)計很靈活,性能也不錯。其有付費版本,但我覺得基于開源版本就可以在性能和功能上滿足開發(fā)者的需求。下面我想先捋一下它的結(jié)構(gòu)設(shè)計,然后結(jié)合實際例子講一下它的使用。

MPAndroidChart的設(shè)計

image

根據(jù)上圖簡明扼要敘述一下各包的作用:

  • animation -- 動畫


    image
  • buffer 數(shù)據(jù)類,用來提高繪制效率,可以看成是緩存。 舉個例子,我們要繪制柱狀圖,其中每個數(shù)據(jù)從點轉(zhuǎn)換成柱狀(四個點,矩形柱狀每個角對應(yīng)一個點),BarBuffer類是怎么做的呢?根據(jù)Entry數(shù)據(jù)的value(點)轉(zhuǎn)成矩形圖像。

... 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 -- 原始數(shù)據(jù)類,與buffer有所不同,這個包里根據(jù)不同圖表封裝了不同的數(shù)據(jù)類型。
  • formatter -- 要繪制的文字的格式(例如x軸的刻度值的格式)
  • highlight -- 高亮線(選中圖表上某個點時出現(xiàn)的高亮狀態(tài))
  • interfaces -- 項目中全局的接口定義(主要是數(shù)據(jù)相關(guān)的的接口定義)
  • jobs -- chart的滑動縮放處理工作
  • listener -- 各種監(jiān)聽器
  • render -- 渲染器, 所有的繪制工作(各種圖表的繪制,軸的繪制,背景分割線,legend/highlight等等)
  • util -- 工具類,最重要的有ViewPortHandler, Transformer

uml類圖結(jié)構(gòu)

項目支持的圖表很多,uml全部呈現(xiàn)顯得比較繁雜,我們就以LineChart(線圖)為例。盡量用最少的信息來理解該庫的設(shè)計,所以我們只包含線圖,不包含x軸/y軸/legend/markview等。
[圖片上傳失敗...(image-eb4cc9-1617104235932)]

chart

chart包里面都是圖表相關(guān)的類,這里以LineChart為例,剖析它的繼承層次,以及每一個父類的職責(zé)。
首先是Chart.class這個最基礎(chǔ)的基類。

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

Chart繼承自ViewGroup,其重寫了onMeasure以及onLayout方法。還定義了一些抽象的模版方法。Chart的主要職責(zé)就是進行measure和layout,以及一些公共行為定義,其它相關(guān)工作交給了對應(yīng)的對象。例如繪制交給了#mRender,手勢監(jiān)聽交給了#mChartTouchListener,縮放/移動處理交給了#mViewPortHandler。
BarLineChartBase繼承自Chart,從類名就可看出它抽象了線圖和柱狀圖的一些公共行為。
LineChart繼承自BarLineChartBase,初始化#mRender

render

渲染器,繪制職責(zé)。LineChartRenderer實現(xiàn)的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

把數(shù)據(jù)值映射成屏幕上的像素點,用matrix實現(xiàn), path的映射主要用到下面?zhèn)z個方法。

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根據(jù)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);
    }

動畫

動畫的實現(xiàn)是使用屬性動畫進行實現(xiàn)的,以線圖為例,屬性動畫的值從0到1,每個繪制周期根據(jù)屬性動畫的值計算要繪制的線圖范圍,這樣就實現(xiàn)了動畫效果。以包分析時的gif圖片所展示的動畫為例,時序圖如下:


image

MPAndroidChart的優(yōu)化

這個庫性能優(yōu)化方便后續(xù)再補。

上面從這個庫的各個方面分析了它的設(shè)計,實現(xiàn)原理。下一篇會介紹基于該庫實現(xiàn)不同的指標(biāo),有的指標(biāo)只是純粹的線圖,只做數(shù)值計算即可,有的指標(biāo)的展現(xiàn)形式有點特殊,就需要對這個庫進行定制化。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 圖表繪制可能是我們項目開發(fā)過程中比較常見的需求,簡單點兒的需求,我們通過自定義控件就能完成,但是像那種比較復(fù)...
    依然范特稀西閱讀 22,692評論 15 105
  • 公司項目使用RN實現(xiàn)的k線圖效果不忍直視,調(diào)試過很多個版本都沒有達到理想的效果,于是想用原生來實現(xiàn)。經(jīng)過調(diào)研選用的...
    vachex閱讀 3,894評論 1 2
  • 開始 添加依賴 在第一步,需要將依賴的庫添加到你的項目中 創(chuàng)建View 想要使用 LineChart,BarCha...
    KMMoonlight閱讀 1,622評論 0 7
  • 開始 添加依賴 在第一步,需要將依賴的庫添加到你的項目中 創(chuàng)建View 想要使用 LineChart,BarCha...
    你吼那么大聲干什么閱讀 17,457評論 1 16
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉(zhuǎn)變要...
    余生動聽閱讀 10,907評論 0 11

友情鏈接更多精彩內(nèi)容