MPAndroidChart在github地址:https://github.com/PhilJay/MPAndroidChart
一.效果图
曲线动态添加数据(X轴为当前时间)
1.png二.实现效果
1.X轴为当前时间,只需要Y轴数据即可
2.X轴的值为字符串,而Entry的构造方法参数全为float public Entry(float x, float y) 所以需要另外定义X轴的值
三.简要核心代码
动态创建一条曲线
/**
* 功能:动态创建一条曲线
*/
private void createLine(LineDataSet lineDataSet, int color, LineData lineData, LineChart lineChart) {
// 初始化线条
initLineDataSet(lineDataSet, color, LineDataSet.Mode.CUBIC_BEZIER);
if (lineData == null) {
lineData = new LineData();
lineData.addDataSet(lineDataSet);
lineChart.setData(lineData);
} else {
lineChart.getLineData().addDataSet(lineDataSet);
}
lineChart.invalidate();
}
动态添加Entry数据
/**
* 动态添加数据
* 在一个LineChart中存放的折线,其实是以索引从0开始编号的
*
* @param yValues y值
*/
public void addEntry(LineData lineData, LineChart lineChart, float yValues, int index) {
// 通过索引得到一条曲线,之后得到折线上当前点的数量
int xCount = lineData.getDataSetByIndex(index).getEntryCount();
SimpleDateFormat df = new SimpleDateFormat("mm:ss");
Entry entry = new Entry(lineData.getEntryCount(), yValues); // 创建一个点
lineData.addEntry(entry, index); // 将entry添加到指定索引处的折线中
Log.d("LineChartDynamicActivity", "addEntry: "+entry.getX());
//通知数据已经改变
lineData.notifyDataChanged();
lineChart.notifyDataSetChanged();
//把yValues移到指定索引的位置
lineChart.moveViewToAnimated(xCount - 4, yValues, YAxis.AxisDependency.LEFT, 1000);
lineChart.invalidate();
}
使用Handle机制进行循环添加数据,以达到不停的动态显示曲线的效果。
核心代码
/**
* 功能:发送开始
*/
public void sendStartAddEntry() {
if (!mDynamicHandler.hasMessages(MSG_START)) { // 判断是否有消息队列此消息,如果没有则发送
mDynamicHandler.sendEmptyMessageDelayed(MSG_START, 1000);
}
}
停止动态显示代码
/**
* 功能:暂停添加点,即移除所有消息
*/
public void sendPauseAddEntry() {
mDynamicHandler.removeCallbacksAndMessages(null);
}
Handle的完整实现
/**
* 功能:自定义Handler,通过弱引用的方式防止内存泄漏
*/
public class DynamicHandler extends Handler {
public static final int MSG_START = 1; // handler消息,开始添加点
WeakReference<LineChartDynamicActivity> mReference;
public DynamicHandler(LineChartDynamicActivity activity) {
mReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
LineChartDynamicActivity lineChartDynamicActivity = mReference.get();
if (lineChartDynamicActivity == null) {
return;
}
switch (msg.what) {
case MSG_START:
lineChartDynamicActivity.addLine1Data(lineChartDynamicActivity.getRandom(30f));
lineChartDynamicActivity.addLine2Data(lineChartDynamicActivity.getRandom(20f));
lineChartDynamicActivity.addLine3Data(lineChartDynamicActivity.getRandom(10f));
lineChartDynamicActivity.sendStartAddEntry();
break;
default:
}
}
}
四.完整的代码实现
布局XML实现
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LineChartDynamicActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.github.mikephil.charting.charts.LineChart
android:id="@+id/demo_linechart"
android:layout_width="match_parent"
android:layout_height="240dp" />
<CheckBox
android:id="@+id/demo_checkbox1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="曲线1" />
<CheckBox
android:id="@+id/demo_checkbox2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="曲线2" />
<CheckBox
android:id="@+id/demo_checkbox3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="曲线3" />
<Button
android:id="@+id/demo_start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="开始"/>
<Button
android:id="@+id/demo_pause"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="暂停"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
完整的Activity实现,
public class LineChartDynamicActivity extends AppCompatActivity implements View.OnClickListener {
public static final int MSG_START = 1; // handler消息,开始添加点
// 曲线编号
public static final int LINE_NUMBER_1 = 0;
public static final int LINE_NUMBER_2 = 1;
public static final int LINE_NUMBER_3 = 2;
/**
* 功能:启动方式
*/
public void startActivity(Context context) {
context.startActivity(new Intent(context, LineChartDynamicActivity.class));
}
private DynamicHandler mDynamicHandler; // 自定义Handler
private Random mRandom = new Random(); // 随机产生点
private DecimalFormat mDecimalFormat = new DecimalFormat("#.00"); // 格式化浮点数位两位小数
private SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");//设置日期格式
private Map<Integer,String> timeDate = new HashMap<Integer,String>();//存储x轴的时间
Button mBtnStart; // 开始添加点
Button mBtnPause; // 暂停添加点
CheckBox mCheckBox1;
CheckBox mCheckBox2;
CheckBox mCheckBox3;
List<CheckBox> mCheckBoxList = new ArrayList<>();
LineChart mLineChart; // 曲线表,存线集合
LineData mLineData; // 线集合,所有折现以数组的形式存到此集合中
XAxis mXAxis; //X轴
YAxis mLeftYAxis; //左侧Y轴
YAxis mRightYAxis; //右侧Y轴
Legend mLegend; //图例
LimitLine mLimitline; //限制线
// Y值数据链表
List<Float> mList1 = new ArrayList<>();
List<Float> mList2 = new ArrayList<>();
List<Float> mList3 = new ArrayList<>();
// Chart需要的点数据链表
List<Entry> mEntries1 = new ArrayList<>();
List<Entry> mEntries2 = new ArrayList<>();
List<Entry> mEntries3 = new ArrayList<>();
// LineDataSet:点集合,即一条线
LineDataSet mLineDataSet1 = new LineDataSet(mEntries1, "曲线1");
LineDataSet mLineDataSet2 = new LineDataSet(mEntries2, "曲线2");
LineDataSet mLineDataSet3 = new LineDataSet(mEntries3, "曲线3");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_line_chart_dynamic);
initView();
initLineChart();
mDynamicHandler = new DynamicHandler(this);
//线条的渐变背景
Drawable drawable1 = ContextCompat.getDrawable(this, R.drawable.fade_red);
setChartFillDrawable(drawable1, mLineDataSet1);
Drawable drawable2 = ContextCompat.getDrawable(this, R.drawable.fade_yellow);
setChartFillDrawable(drawable2, mLineDataSet2);
Drawable drawable3 = ContextCompat.getDrawable(this, R.drawable.fade_blue);
setChartFillDrawable(drawable3, mLineDataSet3);
}
/**
* 功能:产生随机数(小数点两位)
*/
public Float getRandom(Float seed) {
return Float.valueOf(mDecimalFormat.format(mRandom.nextFloat() * seed));
}
/**
* 功能:初始化基本控件,button,checkbox
*/
public void initView() {
mBtnStart = findViewById(R.id.demo_start);
mBtnPause = findViewById(R.id.demo_pause);
mCheckBox1 = findViewById(R.id.demo_checkbox1);
mCheckBox2 = findViewById(R.id.demo_checkbox2);
mCheckBox3 = findViewById(R.id.demo_checkbox3);
mCheckBoxList.add(mCheckBox1);
mCheckBoxList.add(mCheckBox2);
mCheckBoxList.add(mCheckBox3);
mBtnStart.setOnClickListener(this);
mBtnPause.setOnClickListener(this);
mCheckBox1.setOnClickListener(this);
mCheckBox2.setOnClickListener(this);
mCheckBox3.setOnClickListener(this);
}
/**
* 功能:初始化LineChart
*/
public void initLineChart() {
mLineChart = findViewById(R.id.demo_linechart);
mXAxis = mLineChart.getXAxis(); // 得到x轴
mLeftYAxis = mLineChart.getAxisLeft(); // 得到侧Y轴
mRightYAxis = mLineChart.getAxisRight(); // 得到右侧Y轴
mLegend = mLineChart.getLegend(); // 得到图例
mLineData = new LineData();
mLineChart.setData(mLineData);
// 设置图标基本属性
setChartBasicAttr(mLineChart);
//初始化添加第一个时间数据
timeDate.put(0,df.format(System.currentTimeMillis()));
// 设置XY轴
setXYAxis(mLineChart, mXAxis, mLeftYAxis, mRightYAxis);
mXAxis.setValueFormatter(new ValueFormatter() {
@Override
public String getAxisLabel(float value, AxisBase axis) {
return timeDate.get((int) value % timeDate.size());
}
});
// 添加线条
initLine();
// 设置图例
createLegend(mLegend);
// 设置MarkerView
setMarkerView(mLineChart);
}
/**
* 功能:设置图标的基本属性
*/
public void setChartBasicAttr(LineChart lineChart) {
/***图表设置***/
lineChart.setDrawGridBackground(false); //是否展示网格线
lineChart.setDrawBorders(true); //是否显示边界
lineChart.setDragEnabled(true); //是否可以拖动
lineChart.setScaleEnabled(true); // 是否可以缩放
lineChart.setTouchEnabled(true); //是否有触摸事件
//设置XY轴动画效果
//lineChart.animateY(2500);
lineChart.animateX(1500);
}
/**
* 功能:设置XY轴
*/
void setXYAxis(LineChart lineChart, XAxis xAxis, YAxis leftYAxis, YAxis rightYAxis) {
/***XY轴的设置***/
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); //X轴设置显示位置在底部
xAxis.setAxisMinimum(0f); // 设置X轴的最小值
xAxis.setAxisMaximum(30); // 设置X轴的最大值
xAxis.setLabelCount(20, false); // 设置X轴的刻度数量,第二个参数表示是否平均分配
xAxis.setGranularity(1f); // 设置X轴坐标之间的最小间隔
lineChart.setVisibleXRangeMaximum(5);// 当前统计图表中最多在x轴坐标线上显示的总量
//保证Y轴从0开始,不然会上移一点
leftYAxis.setAxisMinimum(0f);
rightYAxis.setAxisMinimum(0f);
leftYAxis.setAxisMaximum(100f);
rightYAxis.setAxisMaximum(100f);
leftYAxis.setGranularity(1f);
rightYAxis.setGranularity(1f);
leftYAxis.setLabelCount(20);
lineChart.setVisibleYRangeMaximum(30, YAxis.AxisDependency.LEFT);// 当前统计图表中最多在Y轴坐标线上显示的总量
lineChart.setVisibleYRangeMaximum(30, YAxis.AxisDependency.RIGHT);// 当前统计图表中最多在Y轴坐标线上显示的总量
leftYAxis.setEnabled(false);
//leftYAxis.setCenterAxisLabels(true);// 将轴标记居中
// leftYAxis.setDrawZeroLine(true); // 原点处绘制 一条线
//leftYAxis.setZeroLineColor(Color.RED);
//leftYAxis.setZeroLineWidth(1f);
}
/**
* 功能:对图表中的曲线初始化,添加三条,并且默认显示第一条
*/
private void initLine() {
createLine(mLineDataSet1, Color.RED, mLineData, mLineChart);
createLine(mLineDataSet2, Color.YELLOW, mLineData, mLineChart);
createLine(mLineDataSet3, Color.BLUE, mLineData, mLineChart);
// mLineData.getDataSetCount() 总线条数
// mLineData.getEntryCount() 总点数
// mLineData.getDataSetByIndex(index).getEntryCount() 索引index处曲线的总点数
// 每条曲线添加到mLineData后,从索引0处开始排列
for (int i = 0; i < mLineData.getDataSetCount(); i++) {
mLineChart.getLineData().getDataSets().get(i).setVisible(false); //
}
showLine(LINE_NUMBER_1);
}
/**
* 功能:根据索引显示或隐藏指定线条
*/
public void showLine(int index) {
mLineChart
.getLineData()
.getDataSets()
.get(index)
.setVisible(mCheckBoxList.get(index).isChecked());
mLineChart.invalidate();
}
/**
* 功能:动态创建一条曲线
*/
private void createLine(LineDataSet lineDataSet, int color, LineData lineData, LineChart lineChart) {
// 初始化线条
initLineDataSet(lineDataSet, color, LineDataSet.Mode.CUBIC_BEZIER);
if (lineData == null) {
lineData = new LineData();
lineData.addDataSet(lineDataSet);
lineChart.setData(lineData);
} else {
lineChart.getLineData().addDataSet(lineDataSet);
}
lineChart.invalidate();
}
/**
* 曲线初始化设置,一个LineDataSet 代表一条曲线
*
* @param lineDataSet 线条
* @param color 线条颜色
* @param mode
*/
private void initLineDataSet(LineDataSet lineDataSet, int color, LineDataSet.Mode mode) {
lineDataSet.setColor(color); // 设置曲线颜色
lineDataSet.setCircleColor(color); // 设置数据点圆形的颜色
lineDataSet.setDrawCircleHole(false);// 设置曲线值的圆点是否是空心
lineDataSet.setLineWidth(1f); // 设置曲线宽度
lineDataSet.setCircleRadius(3f); // 设置折现点圆点半径
lineDataSet.setValueTextSize(10f);
lineDataSet.setDrawFilled(true); //设置曲线图填充
lineDataSet.setFormLineWidth(1f);
lineDataSet.setFormSize(15.f);
if (mode == null) {
//设置曲线展示为圆滑曲线(如果不设置则默认曲线)
lineDataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);
} else {
lineDataSet.setMode(mode);
}
}
/**
* 功能:创建图例
*/
private void createLegend(Legend legend) {
/***曲线图例 标签 设置***/
//设置显示类型,LINE CIRCLE SQUARE EMPTY 等等 多种方式,查看LegendForm 即可
legend.setForm(Legend.LegendForm.CIRCLE);
legend.setTextSize(12f);
//显示位置 左下方
legend.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT);
legend.setOrientation(Legend.LegendOrientation.HORIZONTAL);
//是否绘制在图表里面
legend.setDrawInside(false);
legend.setEnabled(true);
}
/**
* 设置线条填充背景颜色
*
* @param drawable
*/
public void setChartFillDrawable(Drawable drawable, LineDataSet lineDataSet) {
//避免在 initLineDataSet()方法中 设置了 lineDataSet.setDrawFilled(false); 而无法实现效果
lineDataSet.setDrawFilled(true);
lineDataSet.setFillDrawable(drawable);
mLineChart.invalidate();
}
/**
* 设置 可以显示X Y 轴自定义值的 MarkerView
*/
public void setMarkerView(LineChart lineChart) {
DynamicLineChartMarkView mv = new DynamicLineChartMarkView(this);
mv.setChartView(lineChart);
lineChart.setMarker(mv);
lineChart.invalidate();
}
/**
* 动态添加数据
* 在一个LineChart中存放的曲线,其实是以索引从0开始编号的
*
* @param yValues y值
*/
public void addEntry(LineData lineData, LineChart lineChart, float yValues, int index) {
// 通过索引得到一条曲线,之后得到曲线上当前点的数量
int xCount = lineData.getDataSetByIndex(index).getEntryCount();
//添加时间数据
timeDate.put(xCount, df.format(System.currentTimeMillis()));
SimpleDateFormat df = new SimpleDateFormat("mm:ss");
Entry entry = new Entry(lineData.getEntryCount(), yValues); // 创建一个点
lineData.addEntry(entry, index); // 将entry添加到指定索引处的曲线中
Log.d("LineChartDynamicActivity", "addEntry: "+entry.getX());
//通知数据已经改变
lineData.notifyDataChanged();
lineChart.notifyDataSetChanged();
//把yValues移到指定索引的位置
lineChart.moveViewToAnimated(xCount - 4, yValues, YAxis.AxisDependency.LEFT, 1000);
lineChart.invalidate();
}
/**
* 功能:第1条曲线添加一个点
*/
public void addLine1Data(float yValues) {
addEntry(mLineData, mLineChart, yValues, LINE_NUMBER_1);
}
/**
* 功能:第2条曲线添加一个点
*/
public void addLine2Data(float yValues) {
addEntry(mLineData, mLineChart, yValues, LINE_NUMBER_2);
}
/**
* 功能:第3条曲线添加一个点
*/
public void addLine3Data(float yValues) {
addEntry(mLineData, mLineChart, yValues, LINE_NUMBER_3);
}
/**
* 功能:发送开始
*/
public void sendStartAddEntry() {
if (!mDynamicHandler.hasMessages(MSG_START)) { // 判断是否有消息队列此消息,如果没有则发送
mDynamicHandler.sendEmptyMessageDelayed(MSG_START, 1000);
}
}
/**
* 功能:暂停添加点,即移除所有消息
*/
public void sendPauseAddEntry() {
mDynamicHandler.removeCallbacksAndMessages(null);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 清空消息
mDynamicHandler.removeCallbacksAndMessages(null);
mDynamicHandler = null;
// moveViewToAnimated 移动到某个点,有内存泄漏,暂未修复,希望网友可以指着
mLineChart.clearAllViewportJobs();
mLineChart.removeAllViewsInLayout();
mLineChart.removeAllViews();
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.demo_start:
sendStartAddEntry();
break;
case R.id.demo_pause:
sendPauseAddEntry();
break;
case R.id.demo_checkbox1:
showLine(LINE_NUMBER_1);
break;
case R.id.demo_checkbox2:
showLine(LINE_NUMBER_2);
break;
case R.id.demo_checkbox3:
showLine(LINE_NUMBER_3);
break;
default:
}
}
}
里面调用了自定义MarkView,前面具有详线示例,可以参考使用,最后在onDestroy方法里清空消息。
网友评论