Android 自制的一个简单的心电图pulseview
一个自己参考制作出来的心电图,原理很简单,先上图
上面部分是一个view用于显示局部心电图,下面是一个类似进度条的view用于显示全部心电图,当下面的黄色正方形被拖动,上面则会显示正方形所包含区域的局部心电图,起到一个预览的作用。
原理:现将所有数据传入下面的view画出来,再通过监测其ontouch事件,通过正方形的边长计算出其中包含的数据点,并将其取出传入上面的view进行绘图,下面上代码。
这是:上方那个view的代码
package com.healforce.healthapplication.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.util.Log; import android.view.View; import com.healforce.healthapplication.R; import java.util.ArrayList; import java.util.List; public class PulseView extends View {private int XLength; private int XPoint; private int XScale = getResources().getDimensionPixelSize(R.dimen.x2); private int YLength = getResources().getDimensionPixelSize(R.dimen.x120); private int YPoint = getResources().getDimensionPixelSize(R.dimen.x100); private int YScale = getResources().getDimensionPixelSize(R.dimen.x2); private int MaxDataSize = XLength / XScale; private List<Integer> data; private int index; public PulseView(Context paramContext){this(paramContext, null); }public PulseView(Context paramContext, AttributeSet paramAttributeSet){this(paramContext, paramAttributeSet, 0); }public PulseView(Context context, AttributeSet attrs, int resId){super(context, attrs, resId); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PulseView, resId, 0); XScale = typedArray.getDimensionPixelSize(0, getResources().getDimensionPixelSize(R.dimen.x2)); YScale = typedArray.getDimensionPixelSize(1, getResources().getDimensionPixelSize(R.dimen.x20)); typedArray.recycle(); data = new ArrayList<>(); }@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMeasuredWidth(); int height = getMeasuredHeight(); XPoint = getResources().getDimensionPixelSize(R.dimen.x10); YPoint = height - getResources().getDimensionPixelSize(R.dimen.x10); XLength = width - getResources().getDimensionPixelSize(R.dimen.x20); YLength = height - getResources().getDimensionPixelSize(R.dimen.x20); MaxDataSize = (XLength / XScale); Log.i("PUlse",MaxDataSize+""); }public void addDataByPC80B(List<Integer> list) {if (data != null && data.size() > 0){data.clear(); }for (int i = 0; i < list.size(); i++) {data.add(list.get(i)); postInvalidate(); if (data.size() >= MaxDataSize) {data.remove(0); }}}protected void onDraw(Canvas canvas){super.onDraw(canvas); canvas.drawColor(getResources().getColor(R.color.light_gray)); Paint paint = new Paint(); paint.setStyle(Paint.Style.FILL_AND_STROKE); paint.setAntiAlias(true); paint.setColor(getResources().getColor(R.color.yellow)); // 画y轴 canvas.drawLine(XPoint, YPoint - YLength, XPoint, YPoint, paint); // 画水平格子线 for (int i = 0; i * YScale <= YLength; i++) {canvas.drawLine(XPoint, YPoint - i * YScale, XPoint + XLength, YPoint - i * YScale, paint); }// 画x轴 canvas.drawLine(XPoint, YPoint, XPoint + XLength, YPoint, paint); // 画竖直格子线 for (int j = 0; j * YScale <= XLength; j++) {canvas.drawLine(XPoint + j * YScale, YPoint, XPoint + j * YScale, YPoint - YLength, paint); }paint.setColor(Color.RED); paint.setStrokeWidth(2.0F); if (data.size() > 0) {// 画搏动折线 for (int k = 1; k < data.size(); k++){canvas.drawLine(XPoint + (k - 1) * XScale, YPoint - data.get(k - 1), XPoint + k * XScale, YPoint - data.get(k), paint); }// 画刷新线 paint.setColor(getResources().getColor(R.color.light_gray)); paint.setStrokeWidth(5.0F); canvas.drawLine(index * XScale, YPoint - YLength, index * XScale, YPoint, paint); }}public void setData(List<Integer> list) {for (int i = 0;i< list.size(); i++){if (data.size() <= MaxDataSize){data.add(list.get(i)); }else {if (index == MaxDataSize){index = 0; }data.set(index, list.get(i)); index ++ ; }postInvalidate(); }} }
主要调用setData的方法传入数据,即可完成绘制,关于绘制的相关知识,不懂的可以查找其他资料了解,这里就不多说,绘制的时候调用的postInvalidate方法主要是因为,我这个是运行在一个线程中,当不在线程中时可以使用 Invalidate也能达到效果。该控件可以实时传入数据绘制动态波形图,使用的是常见的刷新方法,放完一图刷一图,做的不是很好看,有待优化
接下来是下面的view的代码,很简单:
public class PC80bSectionView extends View {private List<Integer> data = new ArrayList<>(); // 数据源 private float width; // 控件宽 private float height; // 控件高 private PulseView pulseView; // 随手指移动的正方形的中心 private float rec_centerX; private float rec_centerY; private Rect rect = new Rect(); public PC80bSectionView(Context context) {this(context,null,0); }public PC80bSectionView(Context context, AttributeSet attrs) {this(context, attrs,0); }public PC80bSectionView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr); }/** * 画随手指移动的正方形 * @param canvas * @param paint */ private void drawRect(Canvas canvas, Paint paint) {rec_centerY = (height / 2.0F); if (rec_centerX < height / 2.0F) {rec_centerX = (height / 2.0F); }if (rec_centerX > width - height / 2.0F) {rec_centerX = (width - height / 2.0F); }rect.left = ((int) (rec_centerX - height / 2.0F)); rect.right = ((int) (rec_centerX + height / 2.0F)); rect.top = ((int) (rec_centerY + height / 2.0F)); rect.bottom = ((int) (rec_centerY - height / 2.0F)); canvas.drawRect(rect.left, rect.top, rect.right, rect.bottom, paint); }/** * 画波形 * @param canvas * @param paint */ private void drawLine(Canvas canvas, Paint paint) {paint.setColor(Color.GREEN); if (this.data.size() > 1){for (int i = 1; i < data.size(); i++){// 4500表示每次心电仪传输过来的30秒数据的个数 canvas.drawLine((i - 1) * (width / 4500.0F), height - (data.get(i - 1)), i * (width / 4500.0F), height - data.get(i), paint); }}}public void postData(List<Integer> list) {data.addAll(list); postInvalidate(); }public void clearData() {if ((data != null) && (data.size() > 1)){data.clear(); }}protected void onDraw(Canvas canvas) {super.onDraw(canvas); canvas.drawColor(Color.BLACK); Paint paint = new Paint(); paint.setColor(Color.YELLOW); paint.setStrokeWidth(2.0F); drawRect(canvas, paint); drawLine(canvas, paint); }protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec); this.width = getMeasuredWidth(); this.height = getMeasuredHeight(); }public boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:case MotionEvent.ACTION_MOVE:rec_centerX = ((int) event.getX()); invalidate(); // 每次正方形发生移动计算出其中包含区间并将响应区间数据发送至pulseView显示 if (pulseView != null) {List<Integer> list = new ArrayList<>(); for (int i = 0; i < data.size(); i++) {if ((i >= (int) (rect.left * 4500.0F / width)) && (i <= (int) (rect.right * 4500.0F / width))){list.add(data.get(i) * 10); }}pulseView.addDataByPC80B(list); }}return true; }public void setPulseView(PulseView pulseView) {this.pulseView = pulseView; } }
核心代码都在ondraw里面,然后是ontouch时间,非常简单
这就是个人自制的一个简单的显示心电图的控件,本人新手想和大家分享一下,觉得有用就看看,要是觉得不好的就麻烦给点改进意见,初来乍到,请大家不吝赐教
Android 自制的一个简单的心电图pulseview相关推荐
- 使用Android Studio编写一个简单的音乐盒
文章目录 一.知识要点 二.xml代码 activity_main.xml 三.java代码 MainActivity.java MusicService.java 四.运行界面展示 五. 源码Git ...
- Android动态日志,一个简单的Android日志类
Android自带的日志类不支持显示文件名和行号,调试时很不方便.而第三方日志库往往又太重.所以自己对Android自带的日志类做了一个简单的封装,主要是调试时使用,不考虑日志丢失和性能问题.日志的输 ...
- 【Android】Android studio做一个简单的图片浏览器
我们现在布局文件中定义一个简单的线性布局容器,代码如下 activity_main.xml <?xml version="1.0" encoding="utf-8& ...
- Android 逆向笔记 —— 一个简单 CrackMe 的逆向总结
温馨提示 请拖动到文章末尾,长按识别「抽奖」小程序. 在我的印象中,懂逆向的,都是大牛,让我们一起来看看下面这位大牛的学习心得. 无意中在看雪看到一个简单的 CrackMe 应用,正好就着这个例子总结 ...
- Android Jetpack Compose——一个简单的微信界面
一个简单的微信界面 简述 效果视频 底部导航栏 导航元素 导航栏 放入插槽 绘制地图 消息列表 效果图 实现 聊天 效果图 实现 气泡背景 联系人界面 效果图 实现 好友详情 效果图 实现 发现 效果 ...
- Android:设计一个简单的调查问卷
设计一个简单的调查问卷,要求用到TextView,Button,CheckBox,RadioButton,EditText等控件 今天写了一个demo,里面用到了常用的布局,以及常用的几种控件,这里调 ...
- 用Android Studio设计一个简单个性的登录界面
一.用到的组件: LinearLaout.TableLayout.FrameLayout.RelativeLout 二.效果图展示: 三.步骤及过程: 1.首先新建一个Project,并在app -& ...
- Android开发做一个简单的音乐播放器
Android开发如何做一个简单的音乐播放器,首先我们先要知道用到的知识点有哪些. 1.MediaPlayer:可以播放本地资源.sd卡内存资源以及网络uri资源,在这里我们播放sd卡上的音乐资源. ...
- 使用Android studio做一个简单的网站APP
1.首先创建一个空白Android项目 2.然后打开项目,切换为Android视图,这时候会看到三个文件夹,分别是manifests.java.res.首先修改res/layout下的activity ...
最新文章
- LeetCode题组:第21题-合并两个有序链表
- 安装开源在线教育平台edX的一个简单方法
- SpringCloud微服务架构,Spring Cloud 服务治理(Eureka,Consul,Nacos),Ribbon 客户端负载均衡,RestTemplate与OpenFeign实现远程调用
- SpringBoot基础篇AOP之基本使用姿势小结
- html中文乱码_Nginx目录浏览的中文显示问题订正
- c语言点按钮弹窗口,【iOS】按钮点击弹窗
- 拖拽之路(一):自定义QListWidget实现美观的拖拽样式(拖拽即选中)
- 苹果 SwiftUI 踢馆谷歌 Flutter!
- python语言继承6.3节例6-1中的person_第6.3节 Python动态执行之动态编译的compile函数...
- linux下无法安装VMware的解决方法
- Telerik ui kendo for jquery 2022源码版
- 这届年轻人为什么都不爱看电视了?
- 推荐4个爬虫抓包神器
- RGB565的计算颜色表
- 广图登陆知网下载资源教程
- 5G 核心网 Inter NG-RAN node N2 based handover 信令流程
- 点宽与江苏大学建设量化金融实训平台项目
- 【用户投稿】优麒麟社区懒人版本(含软件全家桶)一键安装
- 02.Android崩溃Crash库之App崩溃分析
- P5017 NOIP2018 普及组 摆渡车
热门文章
- 夏普复印机最最最详细的驱动安装教程
- linux是不是在根目录下安装的软件其它用户就可以使用,[转载]Linux下非root用户如何安装软件...
- 国密算法sm3java软实现_国密算法实现
- 清华师姐网盘大曝光(整整400集python学习资料,真香)
- 用html和js编写黑洞数,python_黑洞数
- Macbook pro提示已损坏,无法打开。您应该将它移到废纸篓
- Allegro-默认打开空白PCB文件
- 区块链赋能供应链,不让中间商赚差价!
- 【一、vxWorks6.9】
- eagle-eye介绍