公司项目有个这样的需求:手机通过蓝牙连接体温计,体温计3秒发送一次数据,手机app将接收到的数据用折线图显示出来。当设备断开连接后重连,由于这段时间是接收不到数据的,希望这段时间的折线图留空白。由于MPAndroidChart实现不了留空白的功能,所以通过下载Module导入工程的方式添加依赖。具体效果如下:
首先是辅助类,主要用设置折线图的各种属性,由于是测试类,注释的代码有点多,先上代码吧
package com.example.zhen.thermometerdemo;
import android.content.Context;
import android.graphics.Color;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.LimitLine;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.formatter.ValueFormatter;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Administrator on 2018/9/4.
*/
public class MpChartView {
public static ArrayList<Entry> tempList = new ArrayList<>();
public static String[] dateArray = new String[10];
public static LineChart chart;
public static void getMPChart(LineChart chart, Context context){
// dateArray = new String[]{"10.23", "10.24", "10.25", "10.26", "10.27"};
//自定义设置X轴的值为 => 日期
ValueFormatter formatter = new ValueFormatter() {
@Override
public String getFormattedValue(float value) {
return dateArray[(int) value];
}
};
//设置说明
MpChartView.chart = chart;
chart.setDescription(null);
//设置图例关
chart.getLegend().setEnabled(true);
chart.getLegend().setTextColor(Color.BLACK);
//显示边界
chart.setDrawBorders(false);
//设置显示范围
chart.setVisibleXRangeMaximum(1);
chart.setVisibleYRangeMinimum(10f, YAxis.AxisDependency.LEFT);
// chart.getDescription().setEnabled(true);
//设置透明度
chart.setAlpha(1.0f);
//设置背景色
chart.setBackgroundColor(Color.WHITE);
//设置边框
// chart.setBorderColor(Color.rgb(0, 0, 0));
chart.setGridBackgroundColor(R.color.colorPrimary);
//设置触摸(关闭影响下面3个属性)
chart.setTouchEnabled(true);
//设置是否可以拖拽
chart.setDragEnabled(true);
//设置是否可以缩放
chart.setScaleEnabled(true);
//设置是否能扩大扩小
chart.setPinchZoom(false);
//隐藏点击数据点时的高亮十字
chart.setHighlightPerTapEnabled(false);
chart.setHighlightPerDragEnabled(false);
//获取X轴
XAxis xl = chart.getXAxis();
//启用X轴
xl.setEnabled(true);
xl.setAxisLineWidth(1f);
xl.setAxisLineColor(Color.BLACK);
//设置X轴避免图表或屏幕的边缘的第一个和最后一个轴中的标签条目被裁剪
xl.setAvoidFirstLastClipping(false);
//设置X轴底部显示
xl.setPosition(XAxis.XAxisPosition.BOTTOM);
//设置竖网格
xl.setDrawGridLines(true);
xl.setDrawAxisLine(true);
xl.setLabelCount(10);
//设置X轴文字大小
xl.setTextSize(9f);
//设置X轴单位间隔
xl.setGranularity(1f);
xl.setTextColor(Color.BLACK);
//设置X轴值
xl.setValueFormatter(formatter);
//获取Y轴(左)
YAxis yl = chart.getAxisLeft();
yl.setDrawAxisLine(true);
yl.setSpaceTop(50f);
yl.setSpaceBottom(50f);
yl.setAxisLineColor(Color.BLACK);
yl.setAxisLineWidth(1f);
// yl.setDrawGridLines(false);
// yl.setEnabled(true);
设置Y轴文字在外部显示
yl.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART);
// yl.setTextColor(Color.TRANSPARENT);
Y轴字体
// yl.setTextSize(10f);
设置Y轴最大值
yl.setAxisMaximum(32f);
yl.setTextColor(Color.parseColor("#000000"));
// yl.setYOffset(-(yl.getTextSize()/3));
yl.setTextSize(12f);
设置Y轴起始值
yl.setAxisMinimum(18f);
LimitLine limitLine = new LimitLine(30);
limitLine.setLineColor(Color.RED);
yl.addLimitLine(limitLine);
LimitLine limitLine1 = new LimitLine(22);
limitLine1.setLineColor(Color.YELLOW);
yl.addLimitLine(limitLine1);
// yl.setValueFormatter(new IAxisValueFormatter() {
// @Override
// public String getFormattedValue(float value, AxisBase axis) {
// return (int)value+"℃";
// }
// });
//获取Y轴(右)
YAxis yl2 = chart.getAxisRight();
//禁用右侧Y轴
yl2.setAxisMaximum(32f);
// yl2.setYOffset(-(yl2.getTextSize()/3));
yl2.setAxisMinimum(18f);
yl2.setEnabled(false);
yl2.setTextSize(12f);
yl2.setDrawAxisLine(true);
yl2.setTextColor(Color.parseColor("#000000"));
yl2.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART);
// yl2.setGranularity(1);
// yl2.setValueFormatter(new IAxisValueFormatter() {
// @Override
// public String getFormattedValue(float value, AxisBase axis) {
// return (int)value+"%";
// }
// });
//数据
// tempList = new ArrayList<>();
// tempList.add(new Entry(0, 23));
// tempList.add(new Entry(1, 21));
tempList.add(new Entry(2, 25));
// tempList.add(new Entry(3, 24));
// tempList.add(new Entry(4, 27));
ArrayList<Entry> humitList = new ArrayList<>();
humitList.add(new Entry(0,0));
humitList.add(new Entry(1,0));
humitList.add(new Entry(2,0));
humitList.add(new Entry(3,0));
humitList.add(new Entry(4,0));
humitList.add(new Entry(5,0));
humitList.add(new Entry(6,0));
humitList.add(new Entry(7,0));
humitList.add(new Entry(8,0));
humitList.add(new Entry(9,0));
LineDataSet l1 = new LineDataSet(tempList, "当前温度统计曲线(时/分/秒)");
LineDataSet l2 = new LineDataSet(humitList, "");
l1.setAxisDependency(YAxis.AxisDependency.RIGHT);
设置包括的范围区域填充颜色
// l1.setDrawFilled(true);
// l1.setFillColor(Color.parseColor("#23BBAA"));
l2.setLineWidth(2f);
l2.setColor(Color.parseColor("#ffffff"));
l2.setDrawCircles(false);
//设置线的宽度
l1.setLineWidth(2f);
//设置曲线的颜色
l1.setColor(Color.parseColor("#23BBAA"));
//设置曲率,0.05f-1f 1为折线
// l1.setDrawCubic(true);
l1.setMode(LineDataSet.Mode.CUBIC_BEZIER);
l1.setCubicIntensity(0.1f);
// l1.setCubicIntensity(1);
//设置有圆点
l1.setDrawCircles(true);
//设置小圆点的大小
// l1.setCircleRadius(context.getResources().getDimension(R.dimen.dp_2_5));
//设置圆圈颜色
l1.setCircleColor(Color.parseColor("#23BBAA"));
//填充圆圈内颜色
// l1.setCircleColorHole(Color.rgb(244, 117, 117));
//设置不显示数值
l1.setDrawValues(false);
l1.setValueTextColor(Color.WHITE);
l1.setValueTextSize(10);
List<ILineDataSet> lineDataSetArrayList = new ArrayList<>();
lineDataSetArrayList.add(l1);
lineDataSetArrayList.add(l2);
LineData lineData = new LineData(lineDataSetArrayList);
// lineData
chart.setData(lineData);
//设置XY轴进入动画
// chart.animateXY(800, 800);
//设置最小的缩放
chart.setScaleMinima(1f, 1f);
chart.setClickable(false);
// chart.setBorderColor(Color.BLACK);
// chart.setLeft(-300);
// MyMarkerView mv = new MyMarkerView(context);
// chart.setMarkerView(mv);
// chart.setDrawingCacheEnabled(false);
// chart.setMinOffset(0);
// chart.setExtraBottomOffset(context.getResources().getDimension(R.dimen.dp_5));
chart.offsetTopAndBottom(20);
// chart.setExtraTopOffset(context.getResources().getDimension(R.dimen.dp_10));
// chart.setRight(-300);
// chart.setMaxVisibleValueCount(8);
// Legend l = chart.getLegend();
// l.setPosition(Legend.LegendPosition.BELOW_CHART_LEFT);
// l.setTextSize(10);
// l.setFormSize(10);
// chart.setBackgroundColor(Color.RED);
// l.setYOffset(-(l.getTextSize()/2));
//刷新图表
//chart.invalidate();List<Model> newData = new ArrayList<>();
}
}
接着是主类和布局:
package com.example.zhen.thermometerdemo;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.renderer.LineChartRenderer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
public class MainActivity extends Activity {
private LineChart chart;
private Random random;
private SimpleDateFormat sdf;
private int data;
private String time;
private List<String> timeList;
private List<Integer> dataList;
private List<Integer> emptyList;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
chart.invalidate();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
}
private void initView() {
chart = findViewById(R.id.chart);
timeList = new ArrayList<>();
dataList = new ArrayList<>();
emptyList = new ArrayList<>();
}
private void initData() {
random = new Random();
data = 28;
sdf = new SimpleDateFormat("HH:mm:ss");
time = sdf.format(new Date());
Log.e("MainActivity", time);
for (int i = 0; i < MpChartView.dateArray.length; i++) {
MpChartView.dateArray[i] = time;
MpChartView.tempList.add(new Entry(i,data));
timeList.add(time);
dataList.add(data);
}
MpChartView.getMPChart(chart,this);
new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
MpChartView.tempList.clear();
emptyList.clear();
timeList.remove(0);
dataList.remove(0);
data = random.nextInt(5)+24;
time = sdf.format(new Date());
dataList.add(data);
timeList.add(time);
for (int i = 0; i < dataList.size(); i++) {
MpChartView.dateArray[i] = timeList.get(i);
if (dataList.get(i)<= 25){
// MpChartView.tempList.remove(i);
emptyList.add(i);
}else {
MpChartView.tempList.add(new Entry(i,dataList.get(i)));
}
}
getWhiteColorList(emptyList);
handler.sendEmptyMessage(10);
}
}
}).start();
}
private int count;
private void getWhiteColorList(List<Integer> emptyList){
count = 0;
LineChartRenderer.whiteColorList.clear();
if (emptyList.size() == 1){
LineChartRenderer.whiteColorList.add(emptyList.get(0));
}else if (emptyList.size() > 1){
for (int i = 0; i < emptyList.size(); i++) {
if (LineChartRenderer.whiteColorList.size() == 0){
LineChartRenderer.whiteColorList.add(emptyList.get(i));
}else {
if (emptyList.get(i) - emptyList.get(i-1) == 1){
}else {
LineChartRenderer.whiteColorList.add(emptyList.get(i)-count);
}
}
count++;
}
}
}
}
其中
这个用于初始化折线图,x轴的10个坐标全设为当前时间,y轴为28;
new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
MpChartView.tempList.clear();
emptyList.clear();
timeList.remove(0);
dataList.remove(0);
data = random.nextInt(5)+24;
time = sdf.format(new Date());
dataList.add(data);
timeList.add(time);
for (int i = 0; i < dataList.size(); i++) {
MpChartView.dateArray[i] = timeList.get(i);
if (dataList.get(i)<= 25){
// MpChartView.tempList.remove(i);
emptyList.add(i);
}else {
MpChartView.tempList.add(new Entry(i,dataList.get(i)));
}
}
getWhiteColorList(emptyList);
handler.sendEmptyMessage(10);
}
}
}).start();
这个线程用于生成新的数据并刷新UI,其中用y轴的值随机生成,当生成的值小于26时,模拟为设备掉线
接下来就是源码部分了,源码中是通过画贝塞尔曲线的方法将两个点连接起来的,如果相邻的坐标没有值,就往下一个坐标找。所以如果两个点的连线中间有空值,那么这两点是不需要连线的(其实就是把这条线的颜色变成透明的),修改的对应类为module中的LineChartRenderer.java
源码中是通过for()和cubicPath.cubicTo()画出所有的点的连线,然后再通过paint一次性画出来
而我们需要分段修改连线的颜色,就需要在for()里面将线画出来并分别设置颜色
whiteColorList由主类的方法getWhiteColorList()获得
源码地址:https://download.csdn.net/download/zzagtio/10848818