自定義view——心電圖(根據點(x為時間)還原波形,根據當前顯示影象的最大值動態調整波形,動態增減資料)
阿新 • • 發佈:2019-10-11
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.support.annotation.Nullable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import java.util.ArrayList;
public class ElectrocardiogramView extends View {
private final String TAG = this.getClass().getSimpleName();
//rate是一個(0, 0.5)區間內的值,數值越大,數值點之間的曲線弧度越小。
private float smoothness = 0f;
private float offX = 0f;
private float mStep = 10f;
private int bigHorizontalGridNum = 8;
private int bigVerticalGridNum = 4;
private int xMultiple = 3;
private float smallHorizontalGridWidth;
private float smallVerticalGridHeight;
private float baseLine, maxLevel;
private float halfWidth;
private int mLineWidth;
private float labelXYHeight;
//控制元件寬高
private int mViewWidth, mViewHeight;
private Paint gridLinePaint, linePaint, maxLinePaint;
private TextPaint mLabelXYPaint;
private ArrayList<Point> dataList = new ArrayList<>();
private ArrayList<Point> tempList = new ArrayList<>();
private ArrayList<PointF> mControlPointList = new ArrayList<>();
private Path mLinePath;
private boolean isFirst = false;
public ElectrocardiogramView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
mLinePath = new Path();
mLineWidth = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
gridLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
gridLinePaint.setStrokeJoin(Paint.Join.ROUND);// 筆刷圖形樣式
gridLinePaint.setStrokeCap(Paint.Cap.ROUND);// 設定畫筆轉彎的連線風格
gridLinePaint.setDither(true);//防抖動
gridLinePaint.setShader(null);//設定漸變色
gridLinePaint.setStyle(Paint.Style.FILL);
gridLinePaint.setColor(Color.GREEN);
linePaint = new Paint(gridLinePaint);
linePaint.setColor(Color.YELLOW);
linePaint.setStyle(Paint.Style.STROKE);
linePaint.setStrokeWidth(3);
maxLinePaint = new Paint(gridLinePaint);
maxLinePaint.setColor(Color.WHITE);
maxLinePaint.setStrokeWidth(1);
mLabelXYPaint = new TextPaint();
mLabelXYPaint.setAntiAlias(true);
mLabelXYPaint.setTextAlign(Paint.Align.CENTER);
mLabelXYPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()));
mLabelXYPaint.setColor(Color.RED);
labelXYHeight = mLabelXYPaint.getFontMetrics().bottom - mLabelXYPaint.getFontMetrics().top;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mViewWidth = MeasureSpec.getSize(widthMeasureSpec);
mViewHeight = MeasureSpec.getSize(heightMeasureSpec);
smallHorizontalGridWidth = mViewWidth / (5f * bigHorizontalGridNum);
smallVerticalGridHeight = mViewHeight / (5f * bigVerticalGridNum);
baseLine = mViewHeight / 2f;
maxLevel = mViewHeight / 2.1f;
halfWidth = mViewWidth / 2f;
offX = - mViewWidth;
setMeasuredDimension(mViewWidth, mViewHeight);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
drawGrid(canvas);
drawLine(canvas);
drawMaxAndLine(canvas);
offX += mStep;
postInvalidate();
}
private void drawMaxAndLine(Canvas canvas) {
canvas.save();
canvas.translate(halfWidth, baseLine);
canvas.drawLine(- halfWidth, - maxLevel, halfWidth, -maxLevel, maxLinePaint);
canvas.drawLine(- halfWidth, maxLevel, halfWidth, maxLevel, maxLinePaint);
canvas.drawText("" + max, 0, - maxLevel + labelXYHeight / 3, mLabelXYPaint);
canvas.drawText("" + - max, 0, maxLevel + labelXYHeight / 3, mLabelXYPaint);
canvas.restore();
}
private void drawLine(Canvas canvas) {
Point point;
if (isFirst) {
/*if (dataList.size() <= pointsNum) {
tempList.addAll(dataList);
dataList.clear();
} else {
tempList.addAll(dataList.subList(0, pointsNum));
for (int i = 0; i < pointsNum; i++) {
dataList.remove(0);
}
}*/
while((dataList.size() > 1 && dataList.get(0).x * xMultiple + offX < mViewWidth/* + dataList.get(1).x * xMultiple - dataList.get(0).x * xMultiple*/)
|| dataList.size() == 1) {
tempList.add(dataList.get(0));
Log.e(TAG, "tempFirst:" + dataList.get(0).toString());
dataList.remove(0);
}
max = 1;
for (int i = 0; i < tempList.size(); i++) {
point = tempList.get(i);
point.x = point.x * xMultiple;
if (Math.abs(point.y) > max) max = Math.abs(point.y);
}
for (int i = 0; i < tempList.size(); i++) {
point = tempList.get(i);
point.y = -point.y * maxLevel / max;
}
calculateControlPoint(tempList);
isFirst = false;
} else {
/*if (tempList.get(0).x - offX < -xMultiple && dataList.size() > 0) {
tempList.remove(0);
for (int i = 0; i < tempList.size(); i++) {
point = tempList.get(i);
point.y = -point.y * max / maxLevel;
}
point = dataList.get(0);
point.x = point.x * xMultiple;
tempList.add(point);
dataList.remove(0);
max = 1;
for (int i = 0; i < tempList.size(); i++) {
point = tempList.get(i);
if (Math.abs(point.y) > max) max = Math.abs(point.y);
}
for (int i = 0; i < tempList.size(); i++) {
point = tempList.get(i);
point.y = -point.y * maxLevel / max;
}
calculateControlPoint(tempList);
}*/
while((tempList.size() > 1 && tempList.get(0).x - offX < tempList.get(0).x - tempList.get(1).x)
|| (tempList.size() == 1 && tempList.get(0).x - offX < 0)) {
Log.e(TAG, "tempRemove:" + tempList.get(0).toString());
tempList.remove(0);
}
while (dataList.size() > 0 && tempList.size() > 0 && tempList.get(tempList.size() - 1).x - offX < mViewWidth) {
for (int i = 0; i < tempList.size(); i++) {
point = tempList.get(i);
point.y = -point.y * max / maxLevel;
}
point = dataList.get(0);
point.x = point.x * xMultiple;
tempList.add(point);
dataList.remove(0);
max = 1;
for (int i = 0; i < tempList.size(); i++) {
point = tempList.get(i);
if (Math.abs(point.y) > max) max = Math.abs(point.y);
}
for (int i = 0; i < tempList.size(); i++) {
point = tempList.get(i);
point.y = -point.y * maxLevel / max;
}
Log.e(TAG, "tempAdd:" + tempList.toString());
}
calculateControlPoint(tempList);
}
if (tempList.size() <= 1) return;
canvas.save();
canvas.translate(0, baseLine);
mLinePath.reset();
float x, y;
for (int i = 0; i < tempList.size(); i++) {
point = tempList.get(i);
x = point.x - offX;
y = point.y;
if (i == 0) {
mLinePath.moveTo(x, y);
} else {
PointF controlP1 = mControlPointList.get(i * 2 - 2);
PointF controlP2 = mControlPointList.get(i * 2 - 1);
mLinePath.cubicTo(controlP1.x - offX, controlP1.y, controlP2.x - offX, controlP2.y, x, y);
}
}
canvas.drawPath(mLinePath, linePaint);
canvas.restore();
}
private void drawGrid(Canvas canvas) {
//畫橫線
for (int i = 0; i <= 5 * bigVerticalGridNum; i++) {
if (i % 5 == 0) {
gridLinePaint.setStrokeWidth(1);
} else {
gridLinePaint.setStrokeWidth(0.5f);
}
canvas.drawLine(0, i * smallVerticalGridHeight, mViewWidth, i * smallVerticalGridHeight, gridLinePaint);
}
//畫豎線
for (int i = 0; i <= 5 * bigHorizontalGridNum; i++) {
if (i % 5 == 0) {
gridLinePaint.setStrokeWidth(1);
} else {
gridLinePaint.setStrokeWidth(0.5f);
}
canvas.drawLine(i * smallHorizontalGridWidth, 0, i * smallHorizontalGridWidth, mViewHeight, gridLinePaint);
}
}
private float max = 1f;
public void setData(ArrayList<Point> data, float smoothness) {
// this.max = max;
this.smoothness = smoothness;
dataList.clear();
if (data != null) {
dataList.addAll(data);
}
isFirst = true;
requestLayout();
//postInvalidate();
}
/**
* 計算每個點的三階貝塞爾曲線控制點,第一個點不需要控制點
*
* @return
*/
private void calculateControlPoint(ArrayList<Point> pointList) {
mControlPointList.clear();
if (pointList.size() <= 1) {
return;
}
for (int i = 0; i < pointList.size(); i++) {
Point point = pointList.get(i);
if (i == 0) {//第一項
//新增後控制點
Point nextPoint = pointList.get(1);
float controlX = point.x + (nextPoint.x - point.x) * smoothness;
float controlY = point.y;
mControlPointList.add(new PointF(controlX, controlY));
} else if (i == pointList.size() - 1) {//最後一項
//新增前控制點
Point lastPoint = pointList.get(i - 1);
float controlX = point.x - (point.x - lastPoint.x) * smoothness;
float controlY = point.y;
mControlPointList.add(new PointF(controlX, controlY));
} else {//中間項
Point lastPoint = pointList.get(i - 1);
Point nextPoint = pointList.get(i + 1);
// float k = (nextPoint.y - lastPoint.y) / (nextPoint.x - lastPoint.x);
// float b = point.y - k * point.x;
//新增前控制點
float lastControlX = point.x - (point.x - lastPoint.x) * smoothness;
float lastControlY = point.y;
// float lastControlY = k * lastControlX + b;
mControlPointList.add(new PointF(lastControlX, lastControlY));
//新增後控制點
float nextControlX = point.x + (nextPoint.x - point.x) * smoothness;
float nextControlY = point.y;
// float nextControlY = k * nextControlX + b;
mControlPointList.add(new PointF(nextControlX, nextControlY));
}
}
}
}