网友的使用经验
简友gton的LChart中的折线图在项目中的简单使用
漂亮正文前言
核心:面向对象
面向对象这个名词,对于我们广大的程序员来说,应该是再熟悉不过了。
多数人只是停留在熟悉名词这个阶段,
在遇到一些问题的时候,却不知道采用面向对象的思维去解决它。
当我们跳出老旧的思维枷锁,很多问题也就迎刃而解了 :)
LChart的产生
我们在项目中,经常使用图表来展示数据,这样很形象直观。在Android中,我们大多会使用如下常用的图表库:
- MPAndroidChart (展示丰富,性能好,但是使用较为麻烦,源码难懂,定制起来很困难)
- HelloCharts(使用较为简单,数据量较大的时候,性能差)
以上的图表库,都有着各自的优缺点,项目对图表要求不高的话,使用他们完全没问题。但是要求高的话,这就麻烦了,最后的解决方法是自己重写一个图表 :(
既然项目的图表要求高,那咱就自己写个图表库呗:LChart就是这么产生的。
项目地址:https://github.com/linheimx/LChart
对于LChart,我是以面向对象的方式来分解它,绘制它的,整体来说思路清晰,易于使用。
也许LChart也不适应于你的项目,但我想在这篇文章里面授之以渔,给你带来一定的帮助。
LChart的组织结构
面向图谱中的对象
知道了一个东西由哪几个部分组成,我们内心对这个东西就有数了(不一定了如指掌,有了大概的轮廓也就可以啦)
我们先看一辆自行车的组成结构(帮助我们寻找灵感)
Paste_Image.png从上图,我们大致看出自行车有如下结构:
- 车轮子
- 坐垫
- 车把
对比自行车的结构,那我们的LChart由那几部分组成呢?
基本折线图组成部分
从上面基本的折线图,我们大致可以看出LChart由如下几个部分组成:
- 轴线
- 高亮
- 折线
限于篇幅,我只直观的展示LChart的中3个主要部分,总体上有如下几个部分:
关键组成部分针对以上部分,在我们的代码中,对应为相关的类,这些类封装了相关的信息:
有了这几个对象信息,我们就该考虑绘图了。
根据对象信息进行绘制
LChart是一个自定义View,通过view中的Canvas来进行绘制。
绘制结构我们设计了几个render来负责绘制LChart的几个关键部分。
每个render都有着自己的职责,它向我们封装的对象 Axis,Line等来获取信息,进行各种各样的绘制。
render的体系结构如下:
各种renderLChart的映射:将数据展现到屏幕上
我们的目的是将数据绘制到屏幕上,那么数据和屏幕之间有什么关系呢?
- 数据是有范围的:表现在x方向上范围和y方向上的范围。
- 明确绘制图谱在手机屏幕上的的区域
二者之间的联系是等比例关系,如下图:
等比例关系
在LChart中,我用MappingManager
来维护管理这个关系:
考虑到对图谱的缩放,移动,现将数据视图具体分为:
RectD _maxViewPort; //当前可见的数据范围
RectD _currentViewPort; //最大的数据范围
MappingManager
中具体的数值与像素转换的代码:
知道了数据与手机屏幕中绘图区域的关系。
随便给你一个数据,根据这个关系,你就可以知道这个点在屏幕中的位置了,知道了位置,你用canvas就随便画喽。
LChart的性能优化
1. 减少内存分配
考虑到onDraw()
的频繁调用,尽量的减少内存分配,也就是少new对象。
假设一条数据线上有非常多的点,对每个点都进行映射到屏幕位置的计算,势必要分配很多对象。考虑到是在单个线程:主线程绘制,我采用了单例来保留处理前后的结果。(MpAndroidChart采用对象池来处理内存的分配问题)
2. 减少不必要的绘制
若你把图谱放大:
上图中:蓝色区域为数据的范围,黑色的区域为屏幕上图谱的范围。
很明显,我们只能看到,手机屏幕中的图谱区域显示的部分数据视图。
所以在绘图的时候,只选择绘制可见的数据范围是比较理智的。
3. 减少计算
计算是耗时的,当你的计算非常耗时的时候,图谱就显的非常卡顿,所以减少计算是非常有必要的。
有这样的一种情况:图谱上有很多点,当你点击一个点的时候,要显示这个点的信息。怎么确定是你点击的那个点呢?一般来说是要遍历你图上所有的点,每个点距离你点击的距离更近,那个点就是你想要的点了(HelloCharts是这么处理的),但经验证,这样的计算是非常耗时的,点数稍微一多,谱图就卡顿了。LChart采用二分查找法,快速的命中你想要的点,大大减少计算上的耗时,效果是很理想的。
代码贴上来占据篇幅,地址在项目的data/line.java
这个类下,如有兴趣,自行查看
最后但不是最后
关于LChart,关键的几个地方我大致说了下,详细的使用参见github上的demo。
也许你仍有很多疑惑,So,提出来,后续我也会逐渐的写出来,完善它。
项目地址:https://github.com/linheimx/LChart,欢迎star,issue : )
网友评论
yAxis.setCalWay(Axis.CalWay.perfect);
yAxis.set_ValueAdapter(new DefaultValueAdapter(1));// 默认精度到小数点后2位,现在修改为3位精度
yAxis.setLabelTextSize(33);
yAxis.set_enableUnit(false);
// 数据
line = new Line();
List<Entry> list = addValue();
line.setEntries(list);
line.setLineColor(Color.BLUE);
line.setLineWidth(2);
line.setDrawValue(true);
line.setValueTextSize(26);
line.setCircleColors(new int[]{R.color.colorAccent, R.color.colorPrimary, R.color.colorPrimaryDark});
Lines lines = new Lines();
lines.addLine(line);
lineChart.setLines(lines);
lineChart.setYMax_Min(0, 60);
lineChart.setXAix_MaxMin(-1, 33);
lineChart.post(new Runnable() {
@Override
public void run() {
RectD rectD = lineChart.get_currentViewPort();
rectD.left = -1;
rectD.right = 7;// 第5个数的x数值
lineChart.set_currentViewPort(rectD);
lineChart.postInvalidate();
}
});
这样设置y轴坐标文字大小初始化的时候文字会和坐标重叠
DateValueAdapter xAdapter = new DateValueAdapter(dates);
xAxis.set_ValueAdapter(xAdapter);
line.setEntries(addValue1());
_lineChart.setXAix_MaxMin(-1, dates.size() + 1);
if (dates.size()>7){
_lineChart.post(new Runnable() {
@Override
public void run() {
RectD rectD = _lineChart.get_currentViewPort();
rectD.left = -1;
rectD.right = 7;// 第七个数的x数值
_lineChart.set_currentViewPort(rectD);
_lineChart.postInvalidate();
}
});
}
_lineChart.notifyDataChanged();
然后这里动态更新数据的时候 y轴会向右偏移一定距离
lineChart.setXAix_MaxMin(0, 7); 不行
你先这么处理吧:
lineChart.setLines(lines);
lineChart.post(new Runnable() {
@Override
public void run() {
RectD rectD = lineChart.get_currentViewPort();
rectD.left = 0;
rectD.right = 10;// 第七个数的x数值
lineChart.set_currentViewPort(rectD);
lineChart.postInvalidate();
}
});
再主动设置下x,y轴的最小,最大的范围
lineChart.setXAix_MaxMin(0, 10);
lineChart.setYMax_Min(0, 20);
_lineChart.setCanX_zoom(true);
是一条条折线Line,一条条Line组成了Lines,你把lines告诉linechart就行啦
所以不管你用什么方式,最终是要把数据告诉折线图的。
我会在考虑加进去的,新版本处理这个问题。你可以在github上提交这个issue :)
对应的显示:1月/2日,2月/1日,4月3日。。。
这样的对应关系,你可以用hashmap以键值对的形式保存,采用valueadapter来显示
你的若是时间戳,long--->double是没有损失的,long--->float是有损失的