MPandroidchart源碼查看

背景

金融軟件現(xiàn)在的k線圖功能強(qiáng)大瞪醋,支持各種各樣的指標(biāo)成黄。但是指數(shù)的計(jì)算和繪制其實(shí)是有點(diǎn)復(fù)雜的玻墅。我不炒股也不炒幣介牙,所以對(duì)指標(biāo)背后蘊(yùn)含的市場(chǎng)含義不懂,也不太感興趣澳厢。前不久做了一個(gè)相關(guān)的需求环础,今天總結(jié)一下。

MPAndroidChart

https://github.com/PhilJay/MPAndroidChart 這是github地址剩拢。筆者使用的版本為v2.2.5线得,做了很多定制。 MPAndroidChart功能很強(qiáng)大徐伐,使用者一些特殊的需求也可以自己定制實(shí)現(xiàn)贯钩,因?yàn)樵搸?kù)的設(shè)計(jì)很靈活,性能也不錯(cuò)办素。其有付費(fèi)版本魏保,但我覺得基于開源版本就可以在性能和功能上滿足開發(fā)者的需求。下面我想先捋一下它的結(jié)構(gòu)設(shè)計(jì)摸屠,然后結(jié)合實(shí)際例子講一下它的使用谓罗。

MPAndroidChart的設(shè)計(jì)

image

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

  • animation -- 動(dòng)畫


    image
  • buffer 數(shù)據(jù)類,用來提高繪制效率季二,可以看成是緩存檩咱。 舉個(gè)例子揭措,我們要繪制柱狀圖,其中每個(gè)數(shù)據(jù)從點(diǎn)轉(zhuǎn)換成柱狀(四個(gè)點(diǎn)刻蚯,矩形柱狀每個(gè)角對(duì)應(yīng)一個(gè)點(diǎn))绊含,BarBuffer類是怎么做的呢?根據(jù)Entry數(shù)據(jù)的value(點(diǎn))轉(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有所不同讨便,這個(gè)包里根據(jù)不同圖表封裝了不同的數(shù)據(jù)類型充甚。
  • formatter -- 要繪制的文字的格式(例如x軸的刻度值的格式)
  • highlight -- 高亮線(選中圖表上某個(gè)點(diǎn)時(shí)出現(xiàn)的高亮狀態(tài))
  • interfaces -- 項(xiàng)目中全局的接口定義(主要是數(shù)據(jù)相關(guān)的的接口定義)
  • jobs -- chart的滑動(dòng)縮放處理工作
  • listener -- 各種監(jiān)聽器
  • render -- 渲染器, 所有的繪制工作(各種圖表的繪制霸褒,軸的繪制伴找,背景分割線,legend/highlight等等)
  • util -- 工具類废菱,最重要的有ViewPortHandler, Transformer

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

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

chart

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

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

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

render

渲染器,繪制職責(zé)。LineChartRenderer實(shí)現(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ù)值映射成屏幕上的像素點(diǎn)墩虹,用matrix實(shí)現(xiàn), path的映射主要用到下面?zhèn)z個(gè)方法嘱巾。

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);
}

手勢(shì)處理

image

上圖是手勢(shì)move操作的時(shí)序圖,然后修改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);
    }

動(dòng)畫

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


image

MPAndroidChart的優(yōu)化

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

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市辖佣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌搓逾,老刑警劉巖卷谈,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異霞篡,居然都是意外死亡世蔗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門朗兵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來污淋,“玉大人,你說我怎么就攤上這事余掖〈绫” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵盐欺,是天一觀的道長(zhǎng)赁豆。 經(jīng)常有香客問我,道長(zhǎng)冗美,這世上最難降的妖魔是什么魔种? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮粉洼,結(jié)果婚禮上节预,老公的妹妹穿的比我還像新娘甲抖。我一直安慰自己,他們只是感情好心铃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布准谚。 她就那樣靜靜地躺著,像睡著了一般去扣。 火紅的嫁衣襯著肌膚如雪柱衔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天愉棱,我揣著相機(jī)與錄音唆铐,去河邊找鬼。 笑死奔滑,一個(gè)胖子當(dāng)著我的面吹牛艾岂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播朋其,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼王浴,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了梅猿?” 一聲冷哼從身側(cè)響起氓辣,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎袱蚓,沒想到半個(gè)月后钞啸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喇潘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年体斩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颖低。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡絮吵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出枫甲,到底是詐尸還是另有隱情源武,我是刑警寧澤扼褪,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布想幻,位于F島的核電站,受9級(jí)特大地震影響话浇,放射性物質(zhì)發(fā)生泄漏脏毯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一幔崖、第九天 我趴在偏房一處隱蔽的房頂上張望食店。 院中可真熱鬧渣淤,春花似錦、人聲如沸吉嫩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)自娩。三九已至用踩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間忙迁,已是汗流浹背脐彩。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留姊扔,地道東北人惠奸。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像恰梢,于是被迫代替她去往敵國(guó)和親佛南。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

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