接收數據實時更新的波狀曲線圖
- 2019 年 10 月 7 日
- 筆記
前面做了一個心電圖的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