在項目中需要用到折線圖毡琉,但是某些部分需要自己自定義,這里記錄一下改動點以及如何改動,希望有需要的朋友也能知道大概怎么改貌笨。
需要注意的是這里不講基本用法,百度一下就有很多寫的很好的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袜茧。
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);
}
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軸僻孝,最高點就畫在最高,太丑了
如上圖箭頭守谓,會默認這樣畫穿铆。
但是spaceTop/Bottom無效。
這時候我看到了limitLine這個東西斋荞。因此我打算用limitLine替換y軸虛線
首先設置最大值最小值的時候荞雏,再加一個數(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一下别洪,限制就接觸了
github:https://github.com/Lemniscate317/MPAndroidChartCustomize