MPAndroidChart LineChart 折線圖 你要的都在這裡了
前言
MPAndroidChart已經出了很長的一段時間,相信大家也有所耳聞,自己也使用了有一段時間,固在此寫下文章,根據專案的需求,記錄一些見解與問題,作為參考。望大家取其精華去其糟粕。
最終效果圖
涉及到的問題以及知識點
- 圖表樣式以及基礎資料 (快速入門)
- x軸標籤自定義標籤(Formatting Data Values (ValueFormatter))
- 自定義覆蓋物(MarkerView)
- 自定義多個覆蓋物(MarkerView)
- 預設選中覆蓋物(Highlighting Values)
- 線條的隱藏以及顯示(visible)
- 實現左右滑動
- 資料更新
當前演示 Demo
快速入門
1.編寫佈局檔案
<com.github.mikephil.charting.charts.LineChart
android:id="@+id/chart"
android:layout_width="match_parent"
android:layout_height="195dp"
/>
2.例項化並且,設定x軸和y軸的點
mLineChart = findViewById(R.id.chart);
//1.設定x軸和y軸的點
List<Entry> entries = new ArrayList<>();
for (int i = 0; i < 12; i++)
entries.add(new Entry(i, new Random().nextInt(300)));
3 .把資料賦值到你的線條
LineDataSet dataSet = new LineDataSet(entries, "Label"); // add entries to dataset
4.設定資料重新整理圖表
//3.chart設定資料
LineData lineData = new LineData(dataSet);
mLineChart.setData(lineData);
mLineChart.invalidate(); // refresh
很簡單吧,但是離我們的效果圖還差了好多現在我們開始完善樣式,一步一步去設定
樣式設定
1.線條樣式
LineDataSet dataSet = new LineDataSet(entries, "Label"); // add entries to dataset
dataSet.setColor(Color.parseColor("#7d7d7d"));//線條顏色
dataSet.setCircleColor(Color.parseColor("#7d7d7d"));//圓點顏色
dataSet.setLineWidth(1f);//線條寬度
2.x和y軸樣式
//設定樣式
YAxis rightAxis = mLineChart.getAxisRight();
//設定圖表右邊的y軸禁用
rightAxis.setEnabled(false);
YAxis leftAxis = mLineChart.getAxisLeft();
//設定圖表左邊的y軸禁用
leftAxis.setEnabled(false);
//設定x軸
XAxis xAxis = mLineChart.getXAxis();
xAxis.setTextColor(Color.parseColor("#333333"));
xAxis.setTextSize(11f);
xAxis.setAxisMinimum(0f);
xAxis.setDrawAxisLine(true);//是否繪製軸線
xAxis.setDrawGridLines(false);//設定x軸上每個點對應的線
xAxis.setDrawLabels(true);//繪製標籤 指x軸上的對應數值
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);//設定x軸的顯示位置
xAxis.setGranularity(1f);//禁止放大後x軸標籤重繪
3.隱藏圖例與描述
//透明化圖例
Legend legend = mLineChart.getLegend();
legend.setForm(Legend.LegendForm.NONE);
legend.setTextColor(Color.WHITE);
//隱藏x軸描述
Description description = new Description();
description.setEnabled(false);
mLineChart.setDescription(description);
4.填充資料
//chart設定資料
LineData lineData = new LineData(dataSet);
//是否繪製線條上的文字
lineData.setDrawValues(false);
mLineChart.setData(lineData);
mLineChart.invalidate(); // refresh
效果圖
是不是已經很接近效果圖了,我們在格式化一下x軸標籤
x軸標籤自定義標籤(Formatting Data Values (ValueFormatter))
格式化x軸標籤有好幾種方式,這裡說兩個方法
1.要麼自己實現介面的方式
XAxis xAxis = mLineChart.getXAxis();
xAxis.setValueFormatter(new IAxisValueFormatter() {
@Override
public String getFormattedValue(float value, AxisBase axis) {
return String.valueOf((int) value + 1).concat("月");
}
});
2.要麼用庫已經寫好的類
//準備好每個點對應的x軸數值
List<String> list = new ArrayList<>();
for (int i = 0; i < 12; i++) {
list.add(String.valueOf(i+1).concat("月"));
}
XAxis xAxis = mLineChart.getXAxis();
xAxis.setValueFormatter(new IndexAxisValueFormatter(list));
格式化Y軸也是同樣的道理
效果圖
自定義覆蓋物(MarkerView)
(1) 繼承MarkerView複寫其中的方法就OJBK了
直接上程式碼解釋吧 -v-
public class DetailsMarkerView extends MarkerView {
private TextView mTvMonth;
private TextView mTvChart1;
/**
* 在構造方法裡面傳入自己的佈局以及例項化控制元件
* @param context 上下文
* @param 自己的佈局
*/
public DetailsMarkerView(Context context, int layoutResource) {
super(context, layoutResource);
mTvMonth = findViewById(R.id.tv_chart_month);
mTvChart1 = findViewById(R.id.tv_chart_1);
}
//每次重繪,會呼叫此方法重新整理資料
@Override
public void refreshContent(Entry e, Highlight highlight) {
super.refreshContent(e, highlight);
try {
//收入
if (e.getY() == 0) {
mTvChart1.setText("暫無資料");
} else {
mTvChart1.setText(concat(e.getY(), "支出:"));
}
mTvMonth.setText(String.valueOf((int) e.getX() + 1).concat("月"));
} catch (Exception e1) {
e1.printStackTrace();
}
super.refreshContent(e, highlight);
}
//佈局的偏移量。就是佈局顯示在圓點的那個位置
// -(width / 2) 佈局水平居中
//-(height) 佈局顯示在圓點上方
@Override
public MPPointF getOffset() {
return new MPPointF(-(getWidth() / 2), -getHeight());
}
public String concat(float money, String values) {
return values + new BigDecimal(money).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "元";
}
}
(2) 設定覆蓋物
DetailsMarkerView detailsMarkerView = new DetailsMarkerView(this);
//一定要設定這個玩意,不然到點選到最邊緣的時候不會自動調整佈局
detailsMarkerView.setChartView(mLineChart);
mLineChart.setDetailsMarkerView(detailsMarkerView);
效果圖
自定義多個覆蓋物(MarkerView)
接下來我們繼續完善,達到下面的效果圖
要達到上面的效果,我們可以把它當作3個覆蓋物
就是這個意思
1.先定義好3個覆蓋物,DetailsMarkerView(詳情),PositionMarker (中間的標杆)RoundMarker(圓點)
class DetailsMarkerView extends MarkerView{...}
class PositionMarker extends MarkerView{...}
class RoundMarkerextends MarkerView{...}
2.繼承LineChart,重寫drawMarkers 方法。我們直接把drawMarkers方法直接複製下來,加上自己所需要的MarkerView,然後計算它們的位置即可
public class MyLineChart extends LineChart {
//弱引用覆蓋物物件,防止記憶體洩漏,不被回收
private WeakReference<DetailsMarkerView> mDetailsReference;
private WeakReference<RoundMarker> mRoundMarkerReference;
private WeakReference<PositionMarker> mPositionMarkerReference;
/**
* 所有覆蓋物是否為空
*
* @return TRUE FALSE
*/
public boolean isMarkerAllNull() {
return mDetailsReference.get() == null && mRoundMarkerReference.get() == null && mPositionMarkerReference.get() == null;
}
public void setDetailsMarkerView(DetailsMarkerView detailsMarkerView) {
mDetailsReference = new WeakReference<>(detailsMarkerView);
}
public void setRoundMarker(RoundMarker roundMarker) {
mRoundMarkerReference = new WeakReference<>(roundMarker);
}
public void setPositionMarker(PositionMarker positionMarker) {
mPositionMarkerReference = new WeakReference<>(positionMarker);
}
/**
複製父類的 drawMarkers方法,並且更換上自己的markerView
* draws all MarkerViews on the highlighted positions
*/
protected void drawMarkers(Canvas canvas) {
DetailsMarkerView mDetailsMarkerView = mDetailsReference.get();
RoundMarker mRoundMarker = mRoundMarkerReference.get();
PositionMarker mPositionMarker = mPositionMarkerReference.get();
// if there is no marker view or drawing marker is disabled
if (mDetailsMarkerView == null || mRoundMarker == null || mPositionMarker == 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);
LineDataSet dataSetByIndex = (LineDataSet) getLineData().getDataSetByIndex(highlight.getDataSetIndex());
// check bounds
if (!mViewPortHandler.isInBounds(pos[0], pos[1]))
continue;
float circleRadius = dataSetByIndex.getCircleRadius();
//pos[0], pos[1] x 和 y
// callbacks to update the content
mDetailsMarkerView.refreshContent(e, highlight);
mDetailsMarkerView.draw(canvas, pos[0], pos[1] - mPositionMarker.getHeight());
mPositionMarker.refreshContent(e, highlight);
mPositionMarker.draw(canvas, pos[0] - mPositionMarker.getWidth() / 2, pos[1] - mPositionMarkerl.getHeight());
mRoundMarker.refreshContent(e, highlight);
mRoundMarker.draw(canvas, pos[0] - mRoundMarker.getWidth() / 2, pos[1] + circleRadius - mRoundMarker.getHeight());
}
}
設定覆蓋物 activity主要程式碼
protected void onCreate(Bundle savedInstanceState) {
......
//點選圖表座標監聽
mLineChart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
@Override
public void onValueSelected(Entry e, Highlight h) {
//檢視覆蓋物是否被回收
if (mLineChart.isMarkerAllNull()) {
//重新繫結覆蓋物
createMakerView();
//並且手動高亮覆蓋物
mLineChart.highlightValue(h);
}
}
@Override
public void onNothingSelected() {
}
});
......
}
/**
* 建立覆蓋物
*/
public void createMakerView() {
DetailsMarkerView detailsMarkerView = new DetailsMarkerView(this);
detailsMarkerView.setChartView(mLineChart);
mLineChart.setDetailsMarkerView(detailsMarkerView);
mLineChart.setPositionMarker(new PositionMarker(this));
mLineChart.setRoundMarker(new RoundMarker(this));
}
這樣就大功告成啦!!
預設顯示覆蓋物(Highlighting Values)
可以通過上面的方法,預設顯示覆蓋物,比如
//預設顯示第一個覆蓋物
mLineChart.highlightValue(0,0);
線條的隱藏以及顯示(Highlighting Values)
可以通過LineChart物件獲取到線條LineDataSet實體類。然後呼叫LineDataSet.setVisible(true或者false);,進行隱藏或顯示
mLineChart.getLineData().getDataSets().get(0).setVisible(true);
左右滑動,並動態切換放大倍數
程式碼
//x放大5倍 1f代表不放大
mLineChart.zoomToCenter(5, 1f);
//切記如果要動態的更換倍數,或者還原倍數一定要呼叫下面的這個方法停止慣性滑動
//不然在拖動過程當中是無法更換倍數
BarLineChartTouchListener barLineChartTouchListener = (BarLineChartTouchListener) mLineChart.getOnTouchListener();
barLineChartTouchListener.stopDeceleration();
更新資料
主要的邏輯:
1. 準備要更新的資料來源
2. 檢查是否有LineDataSet 存在
3. 有,則通過LineDataSet 的setValues更換整個座標,或者 data.addEntry(…) 新增一個或者 data.removeEntry(…)刪除一個。
4. 無,則建立LineDataSet ,重新構造資料來源
4. 呼叫mLineChart.invalidate();更新圖表
程式碼例項:
//1,準備要更換的資料
List<Entry> entries = new ArrayList<>();
for (int i = 0; i < 12; i++)
entries.add(new Entry(i, new Random().nextInt(300)));
//2. 獲取LineDataSet線條資料集
List<ILineDataSet> dataSets = mLineChart.getLineData().getDataSets();
//是否存在
if (dataSets != null && dataSets.size() > 0) {
//直接更換資料來源
for (ILineDataSet set : dataSets) {
LineDataSet data = (LineDataSet) set;
data.setValues(entries);
}
} else {
//重新生成LineDataSet線條資料集
LineDataSet dataSet = new LineDataSet(entries, "Label"); // add entries to dataset
dataSet.setDrawCircles(false);
dataSet.setColor(Color.parseColor("#7d7d7d"));//線條顏色
dataSet.setCircleColor(Color.parseColor("#7d7d7d"));//圓點顏色
dataSet.setLineWidth(1f);//線條寬度
LineData lineData = new LineData(dataSet);
//是否繪製線條上的文字
lineData.setDrawValues(false);
mLineChart.setData(lineData);
}
//更新
mLineChart.invalidate();
完
折線圖的內容暫時就那麼多,如果有不懂的可以留言,希望可以幫到大家。
最後附上 Demo