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~

发布了70 篇原创文章 · 获赞 75 · 访问量 27万+

猜你喜欢

转载自blog.csdn.net/qq_22770457/article/details/90349970