美文网首页开源图表库Android控件专题Android
LChart 完全解析 以面向对象的思维来绘制图表

LChart 完全解析 以面向对象的思维来绘制图表

作者: linheimx | 来源:发表于2017-03-03 17:39 被阅读1527次
LChart

网友的使用经验

简友gton的LChart中的折线图在项目中的简单使用

漂亮

正文前言

核心:面向对象
面向对象这个名词,对于我们广大的程序员来说,应该是再熟悉不过了。

多数人只是停留在熟悉名词这个阶段,
在遇到一些问题的时候,却不知道采用面向对象的思维去解决它。
当我们跳出老旧的思维枷锁,很多问题也就迎刃而解了 :)

LChart的产生
我们在项目中,经常使用图表来展示数据,这样很形象直观。在Android中,我们大多会使用如下常用的图表库:

  1. MPAndroidChart (展示丰富,性能好,但是使用较为麻烦,源码难懂,定制起来很困难)
  2. HelloCharts(使用较为简单,数据量较大的时候,性能差)

以上的图表库,都有着各自的优缺点,项目对图表要求不高的话,使用他们完全没问题。但是要求高的话,这就麻烦了,最后的解决方法是自己重写一个图表 :(

既然项目的图表要求高,那咱就自己写个图表库呗:LChart就是这么产生的。
项目地址:https://github.com/linheimx/LChart

对于LChart,我是以面向对象的方式来分解它,绘制它的,整体来说思路清晰,易于使用。
也许LChart也不适应于你的项目,但我想在这篇文章里面授之以渔,给你带来一定的帮助。

LChart的组织结构

面向图谱中的对象

知道了一个东西由哪几个部分组成,我们内心对这个东西就有数了(不一定了如指掌,有了大概的轮廓也就可以啦)

我们先看一辆自行车的组成结构(帮助我们寻找灵感)

Paste_Image.png

从上图,我们大致看出自行车有如下结构:

  1. 车轮子
  2. 坐垫
  3. 车把

对比自行车的结构,那我们的LChart由那几部分组成呢?

基本折线图
组成部分

从上面基本的折线图,我们大致可以看出LChart由如下几个部分组成:

  1. 轴线
  2. 高亮
  3. 折线

限于篇幅,我只直观的展示LChart的中3个主要部分,总体上有如下几个部分:

关键组成部分

针对以上部分,在我们的代码中,对应为相关的类,这些类封装了相关的信息:


有了这几个对象信息,我们就该考虑绘图了。

根据对象信息进行绘制

LChart是一个自定义View,通过view中的Canvas来进行绘制。

我们设计了几个render来负责绘制LChart的几个关键部分。
每个render都有着自己的职责,它向我们封装的对象 Axis,Line等来获取信息,进行各种各样的绘制。

绘制结构

render的体系结构如下:

各种render

LChart的映射:将数据展现到屏幕上

我们的目的是将数据绘制到屏幕上,那么数据和屏幕之间有什么关系呢?

  1. 数据是有范围的:表现在x方向上范围和y方向上的范围。
  2. 明确绘制图谱在手机屏幕上的的区域

二者之间的联系是等比例关系,如下图:


等比例关系

在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 : )

相关文章

  • LChart 完全解析 以面向对象的思维来绘制图表

    网友的使用经验 简友gton的LChart中的折线图在项目中的简单使用 正文前言 核心:面向对象面向对象这个名词,...

  • Python 学习笔记 043

    今日预告 面向对象提升与收发邮件 昨日作业 盖房子解析 :用面向对象的思维来解决该问题(小坑) 人开枪射击子弹的练...

  • 面向对象

    面向过程与面向对象: 内存解析 对象数组的内存解析 匿名对象

  • 浅谈BI

    引言 本文主要面向对象是,对BI完全不了解,或只是初步了解的人。从宏观角度介绍了BI的基本信息,并附以大量的图表作...

  • c#面向对象编程

    面向对象的编程,其实就是用现实中的思维方式来编程。 面向对象编程的3个方式:封装,继承,多态。 而提到面向对象编程...

  • 面向对象的基础

    什么是面向对象? 面向对象是一种编程方法; 面向对象是一种思维方式 面向对象不是一种编程语言 什么是面向对象的思维...

  • Android IOS开发要理解何为面向对象的思维

    何为面向对象的思维? 面向对象 VS面向过程 OOP =》面向对象,全称Object Oriented Progr...

  • 面向对象

    面向对象和面向过程区别:从语言出发:C:完全面向过程C++:一半面向过程,一半面向对象Java:完全面向对象开发方...

  • 为什么 Java 不是纯面向对象语言?

    什么是纯面向对象语言? 纯面向对象语言或完全面向对象语言是指完全面向对象的语言,它支持或具有将程序内的所有内容视为...

  • Groovy学习(三)

    一、面向对象 二、Json解析 三、XML解析 四、文件操作

网友评论

  • yann_x:也想问下高亮线是否可以设置拖拽和拉伸
  • 6460002cb65e:你好,请问高亮线是否可以设置拖拽,不能拖拽的话,我需要修改那一部分的代码?另外贵项目若是增加一个makerview,感觉体验会更加帅气.
  • 小诸葛007:YAxis yAxis = lineChart.get_YAxis();
    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轴坐标文字大小初始化的时候文字会和坐标重叠
    小诸葛007:List<String> dates = initDate1();
    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轴会向右偏移一定距离
  • 小诸葛007:请教下,假如100条数据,每条数据都要显示,x轴固定譬如20dp怎么设置呢?
    linheimx:@小诸葛007 还是显示一部分等分滑动后再显示后面的一部分
    linheimx:@小诸葛007 100个数据一下子都显示出来?
    小诸葛007:就是等分x轴,滑动显示后边的
  • f7b7c532527b:请问一下为啥有些线在y=0的时候不绘制
    f7b7c532527b:@linheimx 我把图发给你看。我有设置从0开始绘制但是没效果。
    linheimx:@神屌大俠丶楊過 和轴线重合了?所以看不到
  • b1ae0644413e:x轴有50条数据,怎么让他一开始就缩放到显示前7条数据呢,
    lineChart.setXAix_MaxMin(0, 7); 不行
    b1ae0644413e:@linheimx 谢谢大佬
    linheimx:这个其实应该暴露简单点的方法来处理的,因为最近比较忙,暂推迟处理
    linheimx:@byc_44a3 可以通过控制viewport来设置图显示的范围。
    你先这么处理吧:

    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();
    }
    });
  • 小张呵呵:你好,这里面的线和点能不能 画出渐变色的效果
    linheimx:@小张呵呵 你是说折线下面有阴影吗?这个阴影是渐变的?这个是有的(line.setFilled(true))
  • 钟艳一生:请问如何设置x轴和y轴的标签从0 开始绘制?
    linheimx:@钟艳一生 设置完数据lineChart.setLines(lines),
    再主动设置下x,y轴的最小,最大的范围
    lineChart.setXAix_MaxMin(0, 10);
    lineChart.setYMax_Min(0, 20);
  • 9565d04b2846:我对你的DEMO进行了修改,但是发现好像没法设置初始的缩放比例。
    linheimx:@乧大的洞洞妖 嗯,这个没有,确实应该考虑加进来的
  • 青檬可乐:楼主 可以加上标注区域的功能吗,或者我自己想加入这个功能 该怎么去实现呢,现在有个需求是如果有一段折线是上升的 那么要把这一段折线所在的图表区域添加一层灰色的阴影(整个图片范围,不仅是折线的下方)
    linheimx:@青檬可乐_ 最好有张图片比较直观
  • summer_lz:能不能绘制 平滑的曲线
    linheimx:需要的话,可以在git上提交个issue,我这两天就把它加上
    linheimx:@summer_lz 不能,之前没考虑过曲线。当然要是加入的话,也很简单。
  • 时光与你皆凉薄:大佬,xy轴的label数据怎么设置的?
    linheimx:@时光与你皆凉薄 是的
    时光与你皆凉薄:@linheimx 看了下,那个adapter是不是只设置的格式化的样式,值是在Axis里的?
    linheimx:@时光与你皆凉薄 为x,y轴设置一下adapter,label的展现由你定。 建议到github上下载运行下demo,很简单的。
  • 简单_eaf1:请问一下:怎么在放大、缩小时候只操作X轴大小不操作Y轴大小呢?
    linheimx:@简单_eaf1 :smile:
    简单_eaf1:@linheimx 嗯嗯实现了,非常感谢!:sunflower:
    linheimx:@简单_eaf1 _lineChart.setCanY_zoom(false);
    _lineChart.setCanX_zoom(true);
  • 琥珀_ff46:博主,这个能实现折线图下面的阴影吗??
    linheimx:已加入:smile:
    琥珀_ff46:@linheimx 好吧
    linheimx:@琥珀_ff46 木有,但是也是比较好加上的。若有需要,最近我会考虑加上的。
  • 柯原哀:楼主,我如果想用sqlite存储一些数据,然后更新数据显示到折线图,这需要怎么实现呢?只用改动下你的添加数据部分吗?
    linheimx:若其中你的一条line有了变化,你要主动去告诉linechart数据变化了(linechart.notifyDataChanged()
    linheimx:至于数据是什么?
    是一条条折线Line,一条条Line组成了Lines,你把lines告诉linechart就行啦
    linheimx:注意:折线图是根据你提供的数据来绘图的。
    所以不管你用什么方式,最终是要把数据告诉折线图的。
  • valooooooooo:有太多数据显示不下,我想先显示一部分,然后滑动时候再动态添加数据,这个能办到吗?
    linheimx:近期我会给出相关的例子 :)
    linheimx:至于产生左右滑动的事件,这个我需要暴露出来给你的。
    linheimx:@valooooooooo 当然可以。假设你有1-100个数据,依次交给图谱10个数据,图谱将绘制出这10个数据。
  • gton:博主用了你的demo在fragment,和viewpage滑动冲突怎么解决
    linheimx:若有这样的需求:LChart滑动到最边缘了,这时候触摸事件交给viewpager。
    我会在考虑加进去的,新版本处理这个问题。你可以在github上提交这个issue :)
    gton:如果我没有match_parent,是可以从margin的地方滑动切换
    linheimx:@magicgt 目前我的解决方案是:LChart接收处理所有的触摸事件。
  • 兜兜里面没有钱:我看了你的demo,我想在x轴显示的是日期,如7/29,这样的,你那个x轴好像只能用float的数据,这个怎么解决呢?
    linheimx:x轴上的数据:1,2,3,4
    对应的显示:1月/2日,2月/1日,4月3日。。。
    这样的对应关系,你可以用hashmap以键值对的形式保存,采用valueadapter来显示
    linheimx:针对轴线上的数据,你可设置xAxis中的 setyValueAdapter方法,设置你的x轴上数据的显示
    linheimx:x,y 现在用的是double
    你的若是时间戳,long--->double是没有损失的,long--->float是有损失的

本文标题:LChart 完全解析 以面向对象的思维来绘制图表

本文链接:https://www.haomeiwen.com/subject/kwyrgttx.html