前言
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
---------------------
作者:LaiShuJie
来源:CSDN
原文:https://blog.csdn.net/a8688555/article/details/80344159
版权声明:本文为博主原创文章,转载请附上博文链接!