Android自定义 View 练习 —— 心电图的绘制

2018-12-11 10:33:40

先上个效果图(画块饼),让你有耐心看完下面内容。




网上已经有不少人,把类似的效果做出来了。我就把自己做出这个效果的思路写出来,希望对想学自定义 View 的朋友有些帮助。


先作一个草图,打开 Windows 自带的“画图”软件,OS X 系统的就找个类似的软件吧。先想好,再来画个草图。我画的草图如下:




看到这,可能有人有疑问了,写自定义 View 怎么不写代码啊,我是来看代码的,最好把完整代码贴出来,画这些图有 * 用啊。有的高手能够在脑海里把很复杂的自定义 View 想象出来。为了说服你最好画一画图,我在这里简单说几句,个人认为画图是个重要的步骤,能事半功倍。想法是代码的灵魂,先想好想法,再去写代码就容易了,类似于“胸中有文章,下笔如涌泉”。假如你是新手阶段,对着别人的代码敲可以有进步,但如果你一直都是对着别人的代码敲然后敲成一个项目的,你自己深想一下,有没有前途?画了图之后,你的思维就被解放了,不会深陷于文字之中,还可能激发出新的想法,你就会想办法实现它。如果你没尝试过的话,可以试一试,顶多就是半个小时的时间,如果你有特别深刻的体验,那恭喜你学会了一种新方式;如果没有,你只是进行了一次风险不大的尝试,人生的惊喜就在于新的尝试。


看着你画的草图,我们可以看到屏幕宽度是已知的,设置大格子的数目后,大格子的宽度也就确定了。这个时候有思绪了,需要横纵两个方向的格子数量。来设置两个全局变量:




horizontalBigGirdNum 为横向的线,即纵向大格子的数量。verticalBigGirdNum 为纵向的线,即横向大格子的数量。


在 XML 文件中使用:




在 xml 中宽 360 dp( match_parent ),高 270 dp,也就是 3 : 4 的关系。 

horizontalBigGirdNum = 6,verticalBigGirdNum = 8,横向八个格子,纵向六个格子,这样划分比较科学。


正式开始写代码。首先初始化画笔。




在 onSizeChanged() 方法调用后,View 的宽高也就确定了。我们可以在这个方法里面来确定每个大格子的宽度。




在 onDraw() 方法中进行绘画。 

将画网格写在一个方法中,并设置标志位,这样可以设置一个 public 方法来设置是否画网格。




画网格的方法:




到这里,网格就出来了。


接下来就是重点,画心电图了。 

画草图。




分析:一条这样的线我们可以用 Path 类来画,画笔 Paint的类型记得设为 Paint.Style.STROKE(不然会很难看)。




没有数据是出不了效果的,我们来生成一些模拟的数据。




如果你需要将真实的心电图数据(比如说从传感器通过蓝牙传送过来的)放进来的话,只要设置一个 public 方法从外面传参数进来就好了。


到了这里,轨迹就能画出来了。但是,它不能像真实心电图那样将心电轨迹慢慢呈现出来,而是一下子就显示出来了。


我们可以加个延时,将心电图的点一个一个慢慢地连接起来。让我来做个比喻。我们都玩过贪吃蛇的游戏,每个蛇要吃的点就是数据源的坐标,没加延时情况下,贪吃蛇的速度很快,我们一眨眼的瞬间它就吃完了,我们就马上看到了整体曲线,而加了延时后,贪吃蛇的速度就慢了,它慢慢地经过点然后吃掉变长,我们也就看到了轨迹形成的过程。


代码实现如下:




增加延时,并且使上面的代码持续地执行。




项目代码: 

完整代码 

(用 AS 的话太大了,还是用 Eclipse 的项目吧)


到此,心电图就基本完成了。这只是一个简单的绘画,要想做的更好,还有许多事情要做,比如说封装入库等。如果你是一个想练习自定义 View 的初学者,我想这篇文章会对你有所启发。如果你是一名高级开发者,我想说,大佬,请指教。

--------------------- 

作者:NotMYida 

来源:CSDN 

原文:https://blog.csdn.net/QingSheng33/article/details/77933977 

版权声明:本文为博主原创文章,转载请附上博文链接!

  • 2019-08-22 13:35:27

    Generator函数的语法

    执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了是状态机还是一个遍历器对象生成函数。 返回遍历器对象,可以依次遍历Generator函数内部的每一个状态。

  • 2019-08-22 16:38:15

    理解JS原型对象与原型链(重要清晰)

    JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象对应拥有一个原型,对象以其原型为模板、从原型继承方法和属性。而同时原型也是对象,它也拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法。

  • 2019-08-22 17:26:21

    详解javaScript的深拷贝

    最开始意识到深拷贝的重要性是在我使用redux的时候(react + redux), redux的机制要求在reducer中必须返回一个新的对象,而不能对原来的对象做改动,事实上,当时我当然不会主动犯这个错误,但很多时候,一不小心可能就会修改了原来的对象,例如:var newObj = obj; newObj.xxx = xxx 实际上,这个时候newObj和obj两个引用指向的是同一个对象,我修改了newObj,实际上也就等同于修改了obj,这,就是我和深浅拷贝的第一次相遇。

  • 2019-08-22 19:14:21

    Android Studio 3.5最新特性

    Android Studio(以下简称为AS) 3.5正式版终于发布了,从第一个bate版本发布到正式版本,历时三个半月。AS一直以来被开发者吐槽,因此谷歌也放慢了版本的变化,对测试版本进行大力度的优化,提高了稳定性。从3.3版本开始,谷歌启动了名为Project Marble的计划,意为谷歌团队致力于使集成开发环境(IDE)的基本功能和流程变得坚如磐石,同时精炼和完善面向用户的功能。而AS 3.5则是Project Marble主要成果的版本,下面来介绍主要成果。

  • 2019-08-27 05:43:13

    Laravel 门面自动补全工具 laravel-ide-helper

    当我们在 PhpStorm 编辑器中,开发 Laravel 框架的项目时,很多类方法都不能自动补全和定位,比如 Facade 门面的方法,DB::table()、Route::get() 等。

  • 2019-08-28 08:28:36

    Js apply,call方法详解,及其apply()方法的妙用

    在给对象参数的情况下,如果参数的形式是数组的时候,比如apply示例里面传递了参数arguments,这个参数是数组类型,并且在调用Person的时候参数的列表是对应一致的(也就是Person和Student的参数列表前两位是一致的) 就可以采用 apply , 如果我的Person的参数列表是这样的(age,name),而Student的参数列表是(name,age,grade),这样就可以用call来实现了,也就是直接指定参数列表对应值的位置(Person.call(this,age,name,grade));