一种在手机屏幕上绘制大量数据动态曲线图的方法
2020-06-10李焕丽
李焕丽
(中国煤炭科工集团太原研究院山西天地煤机装备有限公司 山西省太原市 030006)
随着智能手机的普及,手机APP的使用量急速上升,人们更多的是希望通过手机便捷、快速的浏览信息,因此手机APP开发已经成为当前一项很热门的技术。市面上有这样一类APP,需要将几组上万条数据以曲线的形式显示在手机屏幕上,并且具有左右滑动功能、多点触控缩放功能,如股票走势分析软件、温度趋势分析软件。而此类功能并没有成熟的API供开发者使用[1,2,3,6],如果开发者想要将曲线显示功能灵活的嵌入到自己的软件中,需要做大量的编程工作,这就给开发者带来了一定的困难。
本文针对以上问题,本文参考张军[4]、张伟[5]介绍的在液晶屏或计算机屏幕上显示温度数据曲线的方法后,以2组3万多条温度数据为例,基于Android手机,以Graphics类为基础,设计和开发了一个绘制动态曲线图的API,方便开发者灵活调用并嵌入到程序中,该接口函数实现了4大功能:绘制大量数据曲线图、两点触控缩放曲线、单点触控左右滑动曲线、多组数据的曲线显示。本文依次介绍这4个功能的设计与实现过程。
1 方法概述
Android系统为开发者提供了Graphics类来绘制曲线图,Graphics类包含在System.Drawing名称空间下,此类封装了绘图接口,可以绘制曲线、圆弧、线条、矩形和文本等。表1列出了Graphics类中常用的drawLine方法和drawRect方法的参数及功能描述,这两个方法分别用于绘制线段和矩形框。
本文使用的实验数据为32000条温度数据,而目前主流Android智能手机屏幕的分辨率为1080×2340,假设显示温度数据曲线图的控件的像素为:100×1020(横向像素点×纵向像素点)。数据曲线图效果图如1所示,图中标有“A”的红色虚线是指温度报警上限警示线,标有“B”的红色虚线是指温度报警下限警示线。
2 曲线显示及数据压缩算法
在手机屏幕上绘制曲线图的基本思想是:首先依次将温度数据(记录时间,记录温度)换算成手机屏幕上的像素点(横坐标,纵坐标),然后使用drawLine方法依次将相邻像素点连接,这样就形成了温度曲线,直观形象地描绘出了温度数据的变化趋势。
表1:drawLine、drawRect方法及功能描述
表2:data≤displaydata时的缩放等级定义
表3:data>displaydata时的缩放等级定义
表4:事件发生位置的获取方法
表5:MotionEvent对象事件列表
显然手机一屏并不能描出所有温度数据的像素点,因此在确保正确反映温度数据变化趋势的前提下,使用数据压缩算法,将数据压缩到一定数据量,并放在一屏显示,方便用户观察。数据压缩算法过程如下。
2.1 确定压缩后的数据量
使用Java自带的View.getWidth()方法、View.getHeight()方法分别获取显示温度曲线的控件的横向分辨率和纵向分辨率,使用公式(1)来定义曲线图初始状态下一屏曲线图最多可显示的数据量,即压缩后的数据量,记为displaydata。公式中的wn根据实际情况自定义。
2.2 进行数据压缩
图1:曲线图在手机上的示意图
图2:数据压缩流程图
数据压缩过程流程如图2所示,首先将所有数据(共data个)分为m组,每组n个数据(m、n的计算过程如公式(2)所示),挑选出每组内的最大值和最小值并保证二者相对位置不发生变化,挑选出的数据即为压缩后的数据。挑选最值的流程如图3所示。
2.3 将压缩后的数据绘制成曲线图
通过以上步骤挑选出来的数据记为data_init,构成初始状态下曲线图上的温度数据,将data_init的数据转换成手机屏幕的像素点,依次使用表1中的drawLine(x1,y1,x2,y2,paint)方法连接相邻像素点,直至将所有点用折线连接完毕,即完成了数据曲线的绘制。
2.4 警示线的绘制
曲线图中的警示线具有重要作用,它能快速帮助用户判断数据是否有超出范围。
图1中标有“A”、“B”的红色虚线分别表示温度报警上限警示线和温度报警下限警示线。将图1中显示曲线图的控件截取出来,如图4所示,图中标注了控件的坐标轴(是指标有“控件的x轴”、“控件的y轴”的坐标轴,该坐标轴是Android开发中系统默认的控件坐标轴,是供开发人员绘制图形使用的,坐标轴的原点为控件的左上角)和曲线图坐标系的坐标轴(是指标有“坐标系的x轴”、“坐标系的y轴”的坐标轴,该坐标轴是开发人员绘制出来的呈现给用户的曲线图的坐标轴,原点为控件的左下角)。绘制温度报警警示线需要事先换算出图4中四个点的坐标,分别调用drawLine(x1,y1,x2,y2,paint)方法使用虚线画笔连接两个端点即可。
图3:挑选最值流程图
3 两点触控缩放曲线及缩放算法
当温度数据数量远大于屏幕分辨率时,曲线图仅能提供给用户整体趋势,用户更多的是希望通过触摸屏幕来放大、缩小曲线图,方便的查看某一局部的数据。
图5显示了两点触控常见的三种形式,图中point1、point2是用户初次触摸的两点,point1'、point2'分别是point1、point2移动后的触摸点。
3.1 定义曲线图的缩放等级
第2.1节计算出手机一屏最多可显示的数据量为displaydata,现有温度数据data条。若data≤displaydata,初始状态下一屏可以显示全部记录数据,定义这种情况下的缩放等级为5级,各级的定义如表2所示。若data>displaydata,初始状态下一屏不能显示全部记录数据,因此需要使用第2.1节中提到的数据压缩算法将数据进行压缩,压缩完毕后同样将缩放等级定义为五级,各级的定义如表3所示。
3.2 获取两个触摸点移动前后的坐标
Android系统提供了触摸机制,当用户使用手指或者电容笔对手机屏幕进行触控操作时会创建一个MotionEvent对象,该对象包含关于发生触控的位置、动作、时间等详细信息,并将其传递到系统的View.onTouchEvent()方法中,在该方法中对MotionEvent对象进行分析,确定用户的操作类型,并执行相应的操作。获取MotionEvent对象事件发生位置的方法见表4,MotionEvent对象的主要事件类型及功能描述见表5。
图4:曲线图显示控件
图5:三种常见的触控情况
获取用户触摸的两个点坐标的代码如下:
float px1 = MotionEvent.getX(0); //获取第一个触摸点的横坐标
float py1 = MotionEvent.getY(0); //获取第一个触摸点的纵坐标
float px2 = MotionEvent.getX(1); //获取第二个触摸点的横坐标
float py2 = MotionEvent.getY(1); //获取第二个触摸点的纵坐标
将两个触摸点移动前后的坐标分别记为point1(px1,py1)、point2(px2,py2)和point1'(px1',py1')、point2'(px2',py2')。
3.3 确定缩放焦点及焦点数据
定义图5中point1、point2两点的中心点center(center_x,center_y)为缩放焦点,该点的横、纵坐标计算过程如公式(3)所示。
判断缩放焦点处是否有记录数据,若有,则该数据为焦点数据;若无,需要寻找距离中心点坐标最近的记录数据,将该数据作为焦点数据。确定焦点数据的流程图如图6所示。
3.4 确定缩放等级并进行缩放
分别计算point1、point2两点和point1’、point2’两点之间的距离,计算过程如公式(4)所示。若length'>length,说明用户是要进行放大曲线的操作;反之,若length' 再分别计算出point1、point2两点的移动距离,计算过程如公式(5)所示。取二者的最大值记为l,根据表8找到其对应的缩放等级进行缩放即可。 表6:移动距离与操作的定义表 表7:两组温度数据表 由步骤(5)计算出缩放倍数后根据表5确定数据压缩的组数和每组的数据量,最后使用drawLine方法将其绘制成曲线图。 常见的单点触控情况如图7所示,图中point'是point移动后的触控点,两点的坐标分别为(downXOfFirst,downYOfFirst)、(downXOfSec、downYOfSec)。通 过 计 算downXOfSec与downXOfFirst的差值来判断用户的操作,差值与操作对应表如表6所示。 假设两组温度数据如表7所示,从表中可以看出这两组温度数据的记录时间有重叠的部分(如表中加有下划线的记录时间),因此需要将这两组数据的记录时间进行排序和去重操作,然后再将其作为x坐标轴的刻度来进行显示。 使用改进的败者树算法排序,使用败者树算法进行所条温度曲线显示的步骤如下: 首先,在排序之前将所有记录数据的时间合并到一个List HashSet散列法去重的核心代码如下: HashSet h =new HashSet(x_values); x_values.clear(); x_values.addAll(h); 本文设计的方法实现了在手机屏幕上动态绘制大量数据曲线图的功能,并且支持触控缩放和滑动功能。以接口的方式提供给软件开发者,方便调用,从而将曲线图灵活地嵌入到程序中,具有重要的参考价值。 图6:确定中心点数据流程图 图7:常见的单点触控情况4 单点触控移动曲线及移动算法
5 多组数据的曲线显示
6 结束语