Android——ECG心电图的绘制实现(二)
在上篇Android——ECG心电图的绘制实现博客中,实现了心电图的数据展示绘制。
在之后又新增了 心电图的自动滑动展示 与 实时绘制 功能。效果如下:
自动滑动展示
实时绘制(这里用到了一个timer不断添加数据)
一、自动滑动展示 实现
实现思路:
通过开启定时器实现下标scrollIndex不断增加,以此来计算展示数据的开始下标scrollStartIndex与结束下标scrollEndIndex,来获得一段数据data[]中的展示数据,该段数据刚好填满view的宽度(按比例填满),然后不断的更细视图,实现视觉上的滑动效果(View实际上没有滑动)。如图所示:
代码实现:
首先在initData()初始化数据的时候,调用startScrollTimer()方法,开启一个timer,去不断的更新下标:
private fun startScrollTimer() { timer = Timer() timerTask = object : TimerTask() { override fun run() { if (scrollIndex < data!!.size) { scrollIndex++ } else { scrollIndex = 0 } } } timer!!.schedule(timerTask, 0, 50) }
然后在ondraw中,进行具体的绘制:
// 绘制心电滚动视图 private fun drawHeartScroll(canvas: Canvas) { if (data == null || data!!.size == 0) { return } paint!!.reset() path!!.reset() paint!!.style = Paint.Style.STROKE paint!!.color = Color.parseColor("#31CE32") paint!!.strokeWidth = mGridLinestrokeWidth paint!!.isAntiAlias = true path!!.moveTo(0f, mHeight / 2) var scrollStartIndex = 0 var scrollEndIndex = 0 scrollEndIndex = scrollIndex scrollStartIndex = scrollEndIndex - intervalNumHeart if (scrollStartIndex < 0) { scrollStartIndex = 0 } var nowX: Float var nowY: Float for (i in scrollStartIndex until scrollEndIndex) { nowX = (i - scrollStartIndex) * intervalRowHeart var dataValue = data!![i] if (dataValue > 0) { if (dataValue > MAX_VALUE * 0.8f) { dataValue = MAX_VALUE * 0.8f } } else { if (dataValue < -MAX_VALUE * 0.8f) { dataValue = -(MAX_VALUE * 0.8f) } } nowY = mHeight / 2 - dataValue * intervalColumnHeart path!!.lineTo(nowX, nowY) LogUtils.d("drawHeartScroll $nowX $nowY") } canvas.drawPath(path!!, paint!!) postInvalidate() }
其中,前半部分是对数据下标的整理,后半部分是对整理的data数据进行实时展示。
二、实时绘制刷新refresh 实现
实现思路
这个就比上一个复杂多了。通过showLine(float float) 方法,让外界能输入数据并动态展示在心电图上,也就是说需要实现一个动态递增的Arraylist,然后在具体的绘制中,实现如下所示的数据结构:
第1次添加 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
第2次添加 1 , 2 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
第3次添加 1 , 2 , 3 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
第4次添加 1 , 2 , 3 , 4 , 0 , 0 , 0 , 0 , 0 , 0 ,
第5次添加 1 , 2 , 3 , 4 , 5 , 0 , 0 , 0 , 0 , 0 ,
第6次添加 1 , 2 , 3 , 4 , 5 , 6 , 0 , 0 , 0 , 0 ,
第7次添加 1 , 2 , 3 , 4 , 5 , 6 , 7 , 0 , 0 , 0 ,
第8次添加 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 0 , 0 ,
第9次添加 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 ,
第10次添加 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ,
第11次添加 11 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ,
第12次添加 11 , 12 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ,
第13次添加 11 , 12 , 13 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ,
第14次添加 11 , 12 , 13 , 14 , 5 , 6 , 7 , 8 , 9 , 10 ,
第15次添加 11 , 12 , 13 , 14 , 15 , 6 , 7 , 8 , 9 , 10 ,
第16次添加 11 , 12 , 13 , 14 , 15 , 16 , 7 , 8 , 9 , 10 ,
第17次添加 11 , 12 , 13 , 14 , 15 , 16 , 17 , 8 , 9 , 10 ,
第18次添加 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 9 , 10 ,
第19次添加 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 10 ,
第20次添加 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 ,
第21次添加 21 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 ,
第22次添加 21 , 22 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 ,
第23次添加 21 , 22 , 23 , 14 , 15 , 16 , 17 , 18 , 19 , 20 ,
第24次添加 21 , 22 , 23 , 24 , 15 , 16 , 17 , 18 , 19 , 20 ,
第25次添加 21 , 22 , 23 , 24 , 25 , 16 , 17 , 18 , 19 , 20 ,
第26次添加 21 , 22 , 23 , 24 , 25 , 26 , 17 , 18 , 19 , 20 ,
第27次添加 21 , 22 , 23 , 24 , 25 , 26 , 27 , 18 , 19 , 20 , ....(后面省略)
可以看到要位置一个总长度固定的数据,但是随着数据的增加新的数据是从左往右进行刷新的(对应刷新心电图数据的刷新方式),实现了该结构之后,再去做数据展示就可以实现心电图的刷新绘制了。
为此我专门写了一个测试类去测试:
public class TestClass { private static List refreshList = new ArrayList<>(); private static float[] data; //一排显示的数据 一排10个 private static int intervalNumHeart = 10; private static int showIndex; public static void main(String[] args) { data = new float[intervalNumHeart]; for (int i = 1; i < 100; i++) { refreshList.add((float) i); drawHeartRefresh(); } } private static void drawHeartRefresh() { int nowIndex = refreshList.size(); //当前长度 if (nowIndex < intervalNumHeart) { showIndex = nowIndex - 1; } else { showIndex = (nowIndex - 1) % intervalNumHeart; } for (int i = 0; i < intervalNumHeart; i++) { if (i > refreshList.size() - 1) { break; } if (nowIndex <= intervalNumHeart) { data[i] = refreshList.get(i); } else { int times = (nowIndex - 1) / intervalNumHeart; int temp = times * intervalNumHeart + i; if (temp < nowIndex) { data[i] = refreshList.get(temp); } } } logdata(); } private static void logdata() { String str = ""; for (float temp : data) { int tempInt = (int) temp; str += tempInt + " , "; } Log( "第" +(refreshList.size()) + "次添加 " + str + " " ); } private static void Log(String txt) { System.out.println(txt); }}
代码实现
一样分为两个部分,前半部分实现该结构,后半部分进行具体的绘制:
private fun drawHeartRefresh(canvas: Canvas) { paint!!.reset() path!!.reset() paint!!.style = Paint.Style.STROKE paint!!.color = Color.parseColor("#31CE32") paint!!.strokeWidth = mGridLinestrokeWidth paint!!.isAntiAlias = true path!!.moveTo(0f, mHeight / 2) val nowIndex = if (refreshList == null) 0 else refreshList!!.size //当前长度 if (nowIndex == 0) { return } if (nowIndex < intervalNumHeart) { showIndex = nowIndex - 1 } else { showIndex = (nowIndex - 1) % intervalNumHeart } for (i in 0 until intervalNumHeart) { if (i > refreshList!!.size - 1) { break } if (nowIndex <= intervalNumHeart) { data!![i] = refreshList!![i] } else { val times = (nowIndex - 1) / intervalNumHeart val temp = times * intervalNumHeart + i if (temp < nowIndex) { data?.set(i, refreshList!![temp]) } } } logdata() //绘制出data var nowX: Float var nowY: Float for (i in data!!.indices) { nowX = i * intervalRowHeart var dataValue = data!![i] if (dataValue > 0) { if (dataValue > MAX_VALUE * 0.8f) { dataValue = MAX_VALUE * 0.8f } } else { if (dataValue < -MAX_VALUE * 0.8f) { dataValue = -(MAX_VALUE * 0.8f) } } nowY = mHeight / 2 - dataValue * intervalColumnHeart if (i - 1 == showIndex) { path!!.moveTo(nowX, nowY) } else { path!!.lineTo(nowX, nowY) } } canvas.drawPath(path!!, paint!!) }
近期在把所有写过的自定义view(实用性较强的)整理成一个开源框架,Github地址:
https://github.com/jiangzhengnan/UI#ngui
gradle依赖:
compile 'ng.ngui.ngbase:nguilib:0.0.1'
更多相关文章
- “罗永浩抖音首秀”销售数据的可视化大屏是怎么做出来的呢?
- Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
- 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
- Android(安卓)Handler的详细介绍
- unity调用android语音识别
- android 加密 SQLCipher和Conceal
- 第一篇博客——从《第一行代码》学习笔记开始
- Android中的搜索(search)概述
- Android多文件断点续传(一)——数据封装以及界面实现