1. 程式人生 > >自定義view——心電圖(根據點(x為時間)還原波形,根據當前顯示影象的最大值動態調整波形,動態增減資料)

自定義view——心電圖(根據點(x為時間)還原波形,根據當前顯示影象的最大值動態調整波形,動態增減資料)

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));
            }
        }
    }
}