Android——ECG心电图的绘制实现

Posted 化作孤岛的瓜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android——ECG心电图的绘制实现相关的知识,希望对你有一定的参考价值。

最近在项目中需要使用到的心电Ecg显示效果,本来打算使用sdk方的视图组件的,但是奈何他们的组件问题太多了。比如网格不对齐(强迫症表示这个无法忍),组件不支持静态显示数据等等问题。所以打算自己写一个用来做主页面的心电数据展示。

首先要实现以下几个功能点:

1.网格的大小,颜色可控。

2.心电线条的颜色粗细可控。

3.无论传入的数据源长度是多少,都要均匀的显示在表格上(按一定的比例尺缩放或者扩展)

之后可能会实现动态显示数据的部分,会在本篇博客更新。

技术实现要点:

传入的数据源是String字符串,类似于:"0.101253886148333549,0.001253886148333549,0.003036087844520807,0.002440808573737741"

这样的数据,所以这里要对传入的数据进行处理,根据数据的个数确定屏幕上一共要均匀连线多少个点。

代码实现:


class EcgShowView : View 
    private var mWidth: Float = 0.toFloat()
    private var mHeight: Float = 0.toFloat()
    private var paint: Paint? = null
    private var path: Path? = null
    private var dataStrList: Array<String>? = null
    private var intervalNumHeart: Int = 0
    private var intervalRowHeart: Float = 0.toFloat()
    private var intervalColumnHeart: Float = 0.toFloat()
    private var data: FloatArray? = null
    private var mHeartLinestrokeWidth: Float = 0.toFloat()
    private var row: Int = 0
    private var intervalRow: Float = 0.toFloat()
    private var column: Int = 0
    private var intervalColumn: Float = 0.toFloat()
    private var mGridLinestrokeWidth: Float = 0.toFloat()
    private var mGridstrokeWidthAndHeight: Float = 0.toFloat()

    //心电
    private val MAX_VALUE = 20f //峰值
    private val HEART_LINE_STROKE_WIDTH = 5f

    //网格
    private val GRID_LINE_STROKE_WIDTH = 3f
    private val GRID_WIDTH_AND_HEIGHT = 10f

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) 
        init()
    

    private fun init() 
        paint = Paint()
        path = Path()
        //setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) 

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) 
        super.onLayout(changed, left, top, right, bottom)
        mWidth = measuredWidth.toFloat()
        mHeight = measuredHeight.toFloat()
        mGridLinestrokeWidth = dip2px(GRID_LINE_STROKE_WIDTH).toFloat()
        mGridstrokeWidthAndHeight = dip2px(GRID_WIDTH_AND_HEIGHT).toFloat()
        column = (mWidth / mGridstrokeWidthAndHeight).toInt();

        intervalColumn = mWidth / column
        row = (mHeight / mGridstrokeWidthAndHeight).toInt()

        intervalRow = mHeight / row

        mHeartLinestrokeWidth = dip2px(HEART_LINE_STROKE_WIDTH).toFloat()
        initData()
    

    override fun onDraw(canvas: Canvas) 
        super.onDraw(canvas)
        //绘制网格
        paint!!.style = Paint.Style.STROKE
        paint!!.color = Color.parseColor("#D8D8D8")
        paint!!.strokeWidth = mGridLinestrokeWidth
        paint!!.isAntiAlias = true
        for (i in 0..column) 
            val iTempC = i * intervalColumn
            path!!.moveTo(iTempC, 0f)
            path!!.lineTo(iTempC, mHeight)
        
        for (i in 0..row) 
            path!!.moveTo(0f, i * intervalRow)
            path!!.lineTo(mWidth, i * intervalRow)
        
        canvas.drawPath(path!!, paint!!)
        //绘制心电图
        if (data == null || data!!.size == 0) 
            return
        
        paint!!.reset()
        path!!.reset()
        paint!!.style = Paint.Style.STROKE
        paint!!.color = Color.parseColor("#31CE32")
        paint!!.strokeWidth = mGridLinestrokeWidth
        paint!!.isAntiAlias = true
        path!!.moveTo(0f, mHeight / 2)
        var nowX: Float
        var nowY: Float
        for (i in data!!.indices) 
            nowX = i * intervalRowHeart
            var dataValue = data!![i]
            if (dataValue > 0) 
                if (dataValue > MAX_VALUE * 0.8f) 
                    dataValue = MAX_VALUE * 0.8f
                
             else 
                if (dataValue < -MAX_VALUE * 0.8f) 
                    dataValue = -(MAX_VALUE * 0.8f)
                
            
            nowY = mHeight / 2 - dataValue * intervalColumnHeart
            path!!.lineTo(nowX, nowY)
        
        canvas.drawPath(path!!, paint!!)
    

    fun setData(dataStr: String) 
        dataStrList = dataStr.split(",".toRegex()).dropLastWhile  it.isEmpty() .toTypedArray()
        initData()
    

    private fun initData() 
        try 
            var dataLength = dataStrList!!.size
            if (dataLength > mWidth) 
                dataLength = mWidth.toInt()
            
            data = FloatArray(dataLength)
            for (i in 0 until dataLength) 
                data!![i] = java.lang.Float.parseFloat(dataStrList!![i])
            
            intervalNumHeart = data!!.size
            intervalRowHeart = mWidth / intervalNumHeart
            intervalColumnHeart = mHeight / (MAX_VALUE * 2)
         catch (e: Exception) 
            e.printStackTrace()
        

    

    private fun px2dip(px: Int): Int 
        val scale = context.resources.displayMetrics.density
        return (px / scale + 0.5f).toInt()
    

    private fun dip2px(dipValue: Float): Int 
        val scale = context.resources.displayMetrics.density
        return (dipValue * scale + 0.5f).toInt()
    



Github项目地址:

https://github.com/jiangzhengnan/UI

最近会把实现的UI效果都集中到一个库里面,求start~

以上是关于Android——ECG心电图的绘制实现的主要内容,如果未能解决你的问题,请参考以下文章

Android 根据心电图(ECG)数据分析绘制心电图

ECG/EKG 软件语言建议

深度学习Heartpy心电图分析

使用Python+TensorFlow2构建基于卷积神经网络(CNN)的ECG心电信号识别分类

ECG - ADS1298 使用笔记- 芯片简介(转)

为啥使用 Pandas 的 max 和 min 函数会返回意外结果?