接收蓝牙数据实时更新的波状曲线图

前面做了一个心电图的demo 心电图,结果发现那个心电图是静态的,是应用一启动就已经画好了的,整个页面向左滑动而已

下面我改造了一下,写了一个实时接收数据的动态心电图,网上其他地方也有,但是没有讲到重点

我们先看看效果图

很符合要求吧?只不过我没有到达屏幕的最右边就开始向左滑动是为了理解更方便

其实图中的波状曲线并不是在右边一个一个的增加,而是数据增加,每次都全部重绘的一遍而已,看起来的效果就像右边在增加一样,这点要理解

先看代码

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <View
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:background="#000000" />

    <com.jinke.path.PathView
        android:id="@+id/pathView"
        android:layout_width="match_parent"
        android:layout_height="300dp" />
</RelativeLayout>
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity {

    private Timer timer;
    private TimerTask timerTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final PathView pathView = findViewById(R.id.pathView);
        //模拟实时数据
        timer = new Timer();
        timerTask = new TimerTask() {
            @Override
            public void run() {
                Log.i("BLE", "11111111111111111");
                pathView.setData(-100);
            }
        };
        timer.schedule(timerTask, 0, 1000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        timerTask.cancel();
        timer.cancel();
        timerTask = null;
        timer = null;
    }
}
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

public class PathView extends View {
    //画笔
    protected Paint paint;
    //心电图折线
    protected Path path;
    //自身的大小
    private int width, height;

    int tmpX;
    //折现的颜色
    private int lineColor = Color.parseColor("#76f112");

    private List<Integer> list = new ArrayList<>();

    public PathView(Context context) {
        this(context, null);
    }

    public PathView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PathView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        paint = new Paint();
        path = new Path();
    }

    private void drawPath(Canvas canvas) {
        Log.i("BLE", "drawPath");
        // 重置path
        path.reset();
        paint.reset();
        tmpX = 0;
        path.moveTo(tmpX, height / 2);
        //调节好每个波的X轴距离,尽量和滑动的速度保持一致
        for (int i = 0; i < list.size(); i++) {
            path.lineTo(tmpX + 50, height / 2 + list.get(i));
            path.lineTo(tmpX + 100, height / 2);
            tmpX += 100;
        }
        Log.i("BLE", "TMP=" + tmpX);
        //设置画笔style
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(lineColor);
        paint.setStrokeWidth(5);
        canvas.drawPath(path, paint);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        width = w;
        height = h;
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.i("BLE", "onDraw");
        drawPath(canvas);
        //x轴滑动速度,一开始不滑动,当波形图到达最右边的时候开始滑动
        if (list.size() > 15) {
            scrollBy(1, 0);
        }
    }

    public void setData(int data) {
        Log.i("BLE", "");
        //定期删除历史数据,防止图片过长导致崩溃
        if (list.size() > 30) {
            for (int i = 0; i < 15; i++) {
                list.remove(i);
            }
            scrollTo(0, 0);
        }
        list.add(data);
        Log.i("BLE", "list-size=" + list.size());
        postInvalidate();
    }
}

 自定义View大家都会写,关键是如何让这个自定义View不停的动态重绘呢,网上没有一个说明白的,我来告诉大家

重点:

1.MainActivity里的pathView.setData(-100);方法调用了PathView的setData方法,并传入了更新的值

2.PathView调用了postInvalidate方法,触发重绘

另外在开发中还遇到一个,就是当数据量比较大,View一直向左边滑动,到了某一个时刻,波状图会消失,一片漆黑,看报错原因,是因为滑动的太久,图片拉伸太长导致,手机系统对于长图有一个最长像素值,超过了这个值就会出问题

那么我是如何解决的呢?

1.当数据到达一定量的时候,删掉一部分历史的数据

2.删的同时通过scrollTo(0, 0)方法瞬间滑动最左边

这样就相当于一直在一个固定长度的View上绘图,就不会出问题了,只不过会有少许的偏差,可以调节到滑动的速度和每个波图的X轴距离尽可能的让用户感觉不到

GitHub地址:https://github.com/king1039/Path

欢迎关注我的微信公众号:安卓圈

posted @ 2019-05-20 10:44  嘉禾世兴  阅读(1759)  评论(1编辑  收藏  举报