MPAndroidChart 3.0在項目中需要用到自定義的地方

在項目中需要用到折線圖毡琉,但是某些部分需要自己自定義,這里記錄一下改動點以及如何改動,希望有需要的朋友也能知道大概怎么改貌笨。

需要注意的是這里不講基本用法,百度一下就有很多寫的很好的api講解了

1 多marker

在項目中襟沮,當有多折線的情況下锥惋,手指移動,只會聚焦到最近的點开伏,然后顯示一個marker在頭頂上膀跌。

而在我們的項目中,需要顯示多個marker固灵。如手指在x軸數(shù)字4上假設有4條線就有4個marker捅伤。

原lib是在Chart這個類中的drawMarkers方法中繪制marker

protected void drawMarkers(Canvas canvas) {

        // if there is no marker view or drawing marker is disabled
        if (mMarker == null || !isDrawMarkersEnabled() || !valuesToHighlight())
            return;

        for (int i = 0; i < mIndicesToHighlight.length; i++) {

            Highlight highlight = mIndicesToHighlight[i];

            IDataSet set = mData.getDataSetByIndex(highlight.getDataSetIndex());

            Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]);
            int entryIndex = set.getEntryIndex(e);

            // make sure entry not null
            if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX())
                continue;

            float[] pos = getMarkerPosition(highlight);

            // check bounds
            if (!mViewPortHandler.isInBounds(pos[0], pos[1]))
                continue;

            // callbacks to update the content
            mMarker.refreshContent(e, highlight);

            // draw the marker
            mMarker.draw(canvas, pos[0], pos[1]);
        }
    }

可以看到,是根據(jù)pos0和1去定位在哪個位置巫玻。

于是我們繼承Chart丛忆,并重寫方法。
在我們項目中仍秤,marker需要放置到最高的位置熄诡。因此y軸是0,然后為了與x軸對其诗力,會將x減去marker width的一半凰浮,你們看代碼就懂了

/**
     * draws all MarkerViews on the highlighted positions
     */
    @Override
    protected void drawMarkers(Canvas canvas) {
        //super.drawMarkers(canvas);

        // if there is no marker view or drawing marker is disabled
        if (!isDrawMarkersEnabled() || !valuesToHighlight())
            return;

        for (int i = 0; i < mIndicesToHighlight.length; i++) {

            Highlight highlight = mIndicesToHighlight[i];

            IDataSet set = mData.getDataSetByIndex(highlight.getDataSetIndex());

            Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]);
            int entryIndex = set.getEntryIndex(e);

            // make sure entry not null
            if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX())
                continue;

            float[] pos = getMarkerPosition(highlight);

            // check bounds
            if (!mViewPortHandler.isInBounds(pos[0], pos[1]))
                continue;

            //設置數(shù)據(jù)按照黑黃綠的順序

            Entry blackEntry = null;
            Entry yellowEntry = null;
            Entry greenEntry = null;

            for (int j = 0; j < mData.getDataSetCount(); j++) {
                ILineDataSet dataSetByIndex = mData.getDataSetByIndex(j);
                List<Entry> entriesForXValue = dataSetByIndex.getEntriesForXValue(e.getX());
                if (entriesForXValue.size() > 0) {
                    Entry entry = entriesForXValue.get(0);
                    if (j == 0) {
                        blackEntry = entry;
                    } else if (j == 1) {
                        yellowEntry = entry;
                    } else if (j == 2) {
                        greenEntry = entry;
                    }
                }
            }

            int height = 0;
            int marginTop = 20;

            // callbacks to update the content
            if (blackEntry != null) {
                mBlackMarkView.refreshContent(blackEntry, highlight);

                int width = mBlackMarkView.getWidth();
                float xLeft = pos[0] - width / 2;
                if (xLeft < mViewPortHandler.contentLeft()) {
                    xLeft = mViewPortHandler.contentLeft();
                }
                if (xLeft + width > mViewPortHandler.contentRight()) {
                    xLeft = mViewPortHandler.contentRight() - width;
                }
                // draw the marker
                mBlackMarkView.draw(canvas, xLeft, 0);

                height = mBlackMarkView.getHeight() + marginTop;
            }
            if (yellowEntry != null) {
                mYellowMarkView.refreshContent(yellowEntry, highlight);

                int width = mYellowMarkView.getWidth();
                float xLeft = pos[0] - width / 2;
                if (xLeft < mViewPortHandler.contentLeft()) {
                    xLeft = mViewPortHandler.contentLeft();
                }
                if (xLeft + width > mViewPortHandler.contentRight()) {
                    xLeft = mViewPortHandler.contentRight() - width;
                }
                // draw the marker
                mYellowMarkView.draw(canvas, xLeft, height);

                height += mYellowMarkView.getHeight() + marginTop;
            }
            if (greenEntry != null) {
                mGreenMarkView.refreshContent(greenEntry, highlight);

                int width = mGreenMarkView.getWidth();
                float xLeft = pos[0] - width / 2;
                if (xLeft < mViewPortHandler.contentLeft()) {
                    xLeft = mViewPortHandler.contentLeft();
                }
                if (xLeft + width > mViewPortHandler.contentRight()) {
                    xLeft = mViewPortHandler.contentRight() - width;
                }
                // draw the marker
                mGreenMarkView.draw(canvas, xLeft, height);
            }


        }

在這里我默認會有三條線,因此我會循環(huán)找到012的entry苇本,然后分別繪制marker袜茧。

image.png

2 marker觸及邊緣的時候會看不見

參照上面的源代碼,可以知道我用了一個mViewPortHandler瓣窄。在這個地方mViewPortHandler是管理xy軸內(nèi)部的top left bottom right的笛厦,因此引用這個判斷邊界動態(tài)設置xy即可。

if (greenEntry != null) {
                mGreenMarkView.refreshContent(greenEntry, highlight);

                int width = mGreenMarkView.getWidth();
                float xLeft = pos[0] - width / 2;
                if (xLeft < mViewPortHandler.contentLeft()) {
                    xLeft = mViewPortHandler.contentLeft();
                }
                if (xLeft + width > mViewPortHandler.contentRight()) {
                    xLeft = mViewPortHandler.contentRight() - width;
                }
                // draw the marker
                mGreenMarkView.draw(canvas, xLeft, height);
            }
image.png

3 hightlight默認有橫豎兩軸康栈,我只想要x/y軸

這個官方api有
設置dataset的時候設置即可

set1.setDrawHorizontalHighlightIndicator(false);

4 繪制桌面背景

這個倒是不難
忽略即可

繼承Chart后重寫onDraw
@Override
    protected void onDraw(Canvas canvas) {
        if (mViewPortHandler != null) {
            canvas.drawBitmap(mBitmap, mViewPortHandler.contentRight() - mBitmap.getWidth() - 10, mViewPortHandler.contentTop() + 10, mPaint);
        }
        super.onDraw(canvas);
    }

5當離得很遠的時候递递,觸發(fā)識別別的軸

假設我的手在xy 14 0的地方,而有一條軸線的點在14 1000啥么,而這時候離得比較遠登舞,而13 144這個點離得比你近,這時候系統(tǒng)會選擇13 144這個點作為你的highlight悬荣。

這時候重寫init方法

并重新設置setHighlighter
setHighlighter(new ChartHighlighter(this) {
            @Override
            protected Highlight getHighlightForX(float xVal, float x, float y) {
//                return super.getHighlightForX(xVal, x, y);
                List<Highlight> closestValues = getHighlightsAtXValue(xVal, x, y);

                if (closestValues.isEmpty()) {
                    return null;
                }

//                float leftAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.LEFT);
//                float rightAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.RIGHT);
//
//                YAxis.AxisDependency axis = leftAxisMinDist < rightAxisMinDist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT;
//
//                Highlight detail = getClosestHighlightByPixel(closestValues, x, y, axis, mChart.getMaxHighlightDistance());

                int i = 0;
                int targetIdx = 0;
                float smallestVal = 100;
                for (Highlight highlight : closestValues) {
                    float abs = Math.abs(xVal - highlight.getX());
                    if (abs < smallestVal) {
                        smallestVal = abs;
                        targetIdx = i;
                    }
                    i++;
                }

                Highlight highlight = closestValues.get(targetIdx);


                return highlight;
            }
        });

可以看到原始情況下會調(diào)用getClosestHighlightByPixel這個方法菠秒。
而按照我的需求,我在x軸只要有這個點,就必須選中践叠,因此我重寫并按照我的邏輯選擇返回即可言缤。

6 setSpaceTop,setSpaceBottom無效

在項目中我只能項目顯示3條線禁灼,并且上下留有空隙管挟,為了好看。
而如果設置y軸的最大值最小值并強制設置只有三個點的話弄捕,默認會最底點會占據(jù)x軸僻孝,最高點就畫在最高,太丑了


image.png

如上圖箭頭守谓,會默認這樣畫穿铆。

但是spaceTop/Bottom無效。

這時候我看到了limitLine這個東西斋荞。因此我打算用limitLine替換y軸虛線

image.png

首先設置最大值最小值的時候荞雏,再加一個數(shù),使得上下留有空隙

然后設置limitline平酿,發(fā)現(xiàn)這文字不能畫到y(tǒng)軸左邊

于是開始自定義
并在init方法中重新設置setRendererLeftYAxis方法

setRendererLeftYAxis(new YAxisRenderer(mViewPortHandler, mAxisLeft, mLeftAxisTransformer) {
            @Override
            public void renderLimitLines(Canvas c) {
                //super.renderLimitLines(c);
                List<LimitLine> limitLines = mYAxis.getLimitLines();

                if (limitLines == null || limitLines.size() <= 0)
                    return;

                float[] pts = mRenderLimitLinesBuffer;
                pts[0] = 0;
                pts[1] = 0;
                Path limitLinePath = mRenderLimitLines;
                limitLinePath.reset();

                for (int i = 0; i < limitLines.size(); i++) {

                    LimitLine l = limitLines.get(i);

                    if (!l.isEnabled())
                        continue;

                    int clipRestoreCount = c.save();
                    mLimitLineClippingRect.set(mViewPortHandler.getContentRect());
                    mLimitLineClippingRect.inset(0.f, -l.getLineWidth());
                    c.clipRect(mLimitLineClippingRect);

                    mLimitLinePaint.setStyle(Paint.Style.STROKE);
                    mLimitLinePaint.setColor(l.getLineColor());
                    mLimitLinePaint.setStrokeWidth(l.getLineWidth());
                    mLimitLinePaint.setPathEffect(l.getDashPathEffect());

                    pts[1] = l.getLimit();

                    mTrans.pointValuesToPixel(pts);

                    limitLinePath.moveTo(mViewPortHandler.contentLeft(), pts[1]);
                    limitLinePath.lineTo(mViewPortHandler.contentRight(), pts[1]);

                    c.drawPath(limitLinePath, mLimitLinePaint);
                    limitLinePath.reset();
                    // c.drawLines(pts, mLimitLinePaint);

                    String label = l.getLabel();

                    // if drawing the limit-value label is enabled
                    if (label != null && !label.equals("")) {

                        mLimitLinePaint.setStyle(l.getTextStyle());
                        mLimitLinePaint.setPathEffect(null);
                        mLimitLinePaint.setColor(l.getTextColor());
                        mLimitLinePaint.setTypeface(l.getTypeface());
                        mLimitLinePaint.setStrokeWidth(0.5f);
                        mLimitLinePaint.setTextSize(l.getTextSize());

                        final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label);
                        float xOffset = Utils.convertDpToPixel(4f) + l.getXOffset();
                        float yOffset = l.getLineWidth() + labelLineHeight + l.getYOffset();

                        final LimitLine.LimitLabelPosition position = l.getLabelPosition();

//                        if (position == LimitLine.LimitLabelPosition.RIGHT_TOP) {
//
//                            mLimitLinePaint.setTextAlign(Paint.Align.RIGHT);
//                            c.drawText(label,
//                                    mViewPortHandler.contentRight() - xOffset,
//                                    pts[1] - yOffset + labelLineHeight, mLimitLinePaint);
//
//                        } else if (position == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) {
//
//                            mLimitLinePaint.setTextAlign(Paint.Align.RIGHT);
//                            c.drawText(label,
//                                    mViewPortHandler.contentRight() - xOffset,
//                                    pts[1] + yOffset, mLimitLinePaint);
//
//                        } else if (position == LimitLine.LimitLabelPosition.LEFT_TOP) {
//
//                            mLimitLinePaint.setTextAlign(Paint.Align.LEFT);
//                            c.drawText(label,
//                                    mViewPortHandler.contentLeft() + xOffset,
//                                    pts[1] - yOffset + labelLineHeight, mLimitLinePaint);
//
//                        } else {
//
//                            mLimitLinePaint.setTextAlign(Paint.Align.LEFT);
//                            c.drawText(label,
//                                    mViewPortHandler.offsetLeft() + xOffset,
//                                    pts[1] + yOffset, mLimitLinePaint);
//                        }

                        c.restoreToCount(clipRestoreCount);
                        clipRestoreCount = c.save();


                        Rect rect = new Rect();
                        mLimitLinePaint.getTextBounds(label, 0, label.length(), rect);

                        mLimitLinePaint.setTextAlign(Paint.Align.LEFT);
                        c.drawText(label,
                                mViewPortHandler.contentLeft() - xOffset - rect.width(),
                                pts[1] + rect.height() / 2, mLimitLinePaint);
                    }

                    c.restoreToCount(clipRestoreCount);
                }
            }
        });

由于上面設置了clipRect方法凤优,所以我們畫不出去。因此我們有模有樣染服,先save一下别洪,限制就接觸了


image.png

github:https://github.com/Lemniscate317/MPAndroidChartCustomize

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叨恨,一起剝皮案震驚了整個濱河市柳刮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌痒钝,老刑警劉巖秉颗,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異送矩,居然都是意外死亡蚕甥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門栋荸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來菇怀,“玉大人,你說我怎么就攤上這事晌块“担” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵匆背,是天一觀的道長呼伸。 經(jīng)常有香客問我,道長钝尸,這世上最難降的妖魔是什么括享? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任搂根,我火速辦了婚禮,結(jié)果婚禮上铃辖,老公的妹妹穿的比我還像新娘剩愧。我一直安慰自己,他們只是感情好娇斩,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布隙咸。 她就那樣靜靜地躺著,像睡著了一般成洗。 火紅的嫁衣襯著肌膚如雪五督。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天瓶殃,我揣著相機與錄音充包,去河邊找鬼。 笑死遥椿,一個胖子當著我的面吹牛基矮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播冠场,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼家浇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了碴裙?” 一聲冷哼從身側(cè)響起钢悲,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎舔株,沒想到半個月后莺琳,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡载慈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年惭等,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片办铡。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡辞做,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寡具,到底是詐尸還是另有隱情秤茅,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布晒杈,位于F島的核電站嫂伞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜帖努,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一撰豺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拼余,春花似錦污桦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亭姥,卻和暖如春稼钩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背达罗。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工坝撑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人粮揉。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓巡李,卻偏偏與公主長得像,于是被迫代替她去往敵國和親扶认。 傳聞我的和親對象是個殘疾皇子侨拦,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

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