生活随笔
收集整理的這篇文章主要介紹了
Android如何自定义一个心电图控件?
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
摘要:在我們的日常生活中,通常需要用儀器測量心率數據,來觀測身體是否在一個健康的范圍之內, 下面的心率圖就是用一個胎心儀測量的一個寶寶心率圖。
自定義控件如下圖所示:
畫好這樣的一個心電圖視圖需要自定義一個控件具體步驟如下:
一 自定義屬性
在attrs.xml下自定義如下屬性,在我們使用此自定義控件的時候,可以根據需求改變樣式。
<declare
-styleable name
="chartView"><!-- xy坐標軸顏色
--><attr name
="xylinecolor" format
="color" /><!-- xy坐標軸寬度
--><attr name
="xylinewidth" format
="dimension" /><!-- xy坐標軸文字顏色
--><attr name
="xytextcolor" format
="color" /><!-- xy坐標軸文字大小
--><attr name
="xytextsize" format
="dimension" /><!-- 折線圖中折線的顏色
--><attr name
="linecolor" format
="color" /><!-- x軸各個坐標點水平間距
--><attr name
="interval" format
="dimension" /><!-- 背景顏色
--><attr name
="bgcolor" format
="color" /><!--是否在ACTION_UP時,根據速度進行自滑動,建議關閉,過于占用GPU
--><attr name
="isScroll" format
="boolean" /></declare
-styleable
>
使用自定義屬性
TypedArray array
= context
.obtainStyledAttributes(attrs
, R
.styleable
.chartView
, defStyleAttr
, 0);int count
= array
.getIndexCount();for (int i
= 0; i
< count
; i
++) {int attr
= array
.getIndex(i
);switch (attr
) {case R
.styleable
.chartView_xylinecolor
://xy坐標軸顏色xylinecolor
= array
.getColor(attr
, xylinecolor
);break;case R
.styleable
.chartView_xylinewidth
://xy坐標軸寬度xylinewidth
= (int) array
.getDimension(attr
, TypedValue
.applyDimension(TypedValue
.COMPLEX_UNIT_PX
, xylinewidth
, getResources().getDisplayMetrics()));break;case R
.styleable
.chartView_xytextcolor
://xy坐標軸文字顏色xytextcolor
= array
.getColor(attr
, xytextcolor
);break;case R
.styleable
.chartView_xytextsize
://xy坐標軸文字大小xytextsize
= (int) array
.getDimension(attr
, TypedValue
.applyDimension(TypedValue
.COMPLEX_UNIT_PX
, xytextsize
, getResources().getDisplayMetrics()));break;case R
.styleable
.chartView_linecolor
://折線圖中折線的顏色linecolor
= array
.getColor(attr
, linecolor
);break;case R
.styleable
.chartView_interval
://x軸各個坐標點水平間距interval
= (int) array
.getDimension(attr
, TypedValue
.applyDimension(TypedValue
.COMPLEX_UNIT_PX
, interval
, getResources().getDisplayMetrics()));break;case R
.styleable
.chartView_bgcolor
: bgcolor
= array
.getColor(attr
, bgcolor
);break;case R
.styleable
.chartView_isScroll
://是否在ACTION_UP時,根據速度進行自滑動isScroll
= array
.getBoolean(attr
, isScroll
);break;}}array
.recycle();
二 繪制圖形
繪制X軸以及上方的水平虛線,水平軸的時間文字及描點。
canvas
.drawLine(xOri
, yOri
+ xylinewidth
/ 2, width
, yOri
+ xylinewidth
/ 2, xyPaint
);for (int i
= 0; i
< xValue
.size(); i
++) {float x
= xInit
+ interval
* i
;if (i
== 0 && x
- interval
>= xOri
) {String text
= TimeUtil
.getChartTime(0);Rect rect
= getTextBounds(text
, xyTextPaint
);canvas
.drawText(text
, 0, text
.length(), x
- interval
- rect
.width() / 2, yOri
+ xylinewidth
+ dpToPx(4) + rect
.height(), xyTextPaint
);canvas
.drawCircle(xOri
, yOri
, radiu
, linePaint
);}if (x
>= xOri
) {xyTextPaint
.setColor(xytextcolor
);canvas
.drawLine(x
, yOri
- yLength
* (yValue
.size() - 1) + xylinewidth
/ 2, x
, yOri
, (i
+ 1) % 3 == 0 ? xyPaint
: xyredPaint
);if ((i
+ 1) % 3 == 0) {String text
= TimeUtil
.getChartTime((i
+ 1) * perLengTime
);Rect rect
= getTextBounds(text
, xyTextPaint
);canvas
.drawText(text
, 0, text
.length(), x
- rect
.width() / 2 - dpToPx(2), yOri
+ xylinewidth
+ dpToPx(4) + rect
.height(), xyTextPaint
);canvas
.drawCircle(x
, yOri
, radiu
, linePaint
);}}}
繪制Y軸以及上方的水平虛線,水平軸的心率數值文字及描點。
canvas
.drawLine(xOri
- xylinewidth
/ 2, yOri
- yLength
* (yValue
.size() - 1), xOri
- xylinewidth
/ 2, yOri
, xyPaint
);for (int i
= 0; i
< yValue
.size(); i
++) {canvas
.drawLine(xOri
, yOri
- yLength
* i
+ xylinewidth
/ 2, width
, yOri
- yLength
* i
+ xylinewidth
/ 2, xyPaint
);canvas
.drawCircle(xOri
, yOri
- yLength
* i
, radiu
, linePaint
);if (i
> 0) {canvas
.drawLine(xOri
, yOri
- yLength
* i
+ xylinewidth
/ 2 + 1.0f / 3 * yLength
, width
, yOri
- yLength
* i
+ xylinewidth
/ 2 + 1.0f / 3 * yLength
, xyredPaint
);canvas
.drawLine(xOri
, yOri
- yLength
* i
+ xylinewidth
/ 2 + 2.0f / 3 * yLength
, width
, yOri
- yLength
* i
+ xylinewidth
/ 2 + 2.0f / 3 * yLength
, xyredPaint
);}xyTextPaint
.setColor(xytextcolor
);String text
= yValue
.get(i
) + "";Rect rect
= getTextBounds(text
, xyTextPaint
);canvas
.drawText(text
, 0, text
.length(), xOri
- xylinewidth
- dpToPx(3) - rect
.width(), yOri
- yLength
* i
+ rect
.height() / 2, xyTextPaint
);int length
= xValue
.size() / 12;if (length
> 0) {for (int j
= 1; j
<= length
; j
++) {float x1
= xInit
+ interval
* j
* 12;if (x1
> xOri
+ interval
* 6)canvas
.drawText(text
, 0, text
.length(), x1
- interval
- rect
.width() - dpToPx(3), yOri
- yLength
* i
+ rect
.height() / 2, xyTextPaint
);}}}
繪制心率單位
private void drawUnit(Canvas canvas
, int yLength
) {xyTextPaint
.setTextSize(dpToPx(6));String text
= "FHR/bpm";Rect rect
= getTextBounds(text
, xyTextPaint
);canvas
.drawText("FHR/bpm", 0, text
.length(), xOri
- xylinewidth
- dpToPx(3) - rect
.width(), yOri
- yLength
* (1.0f * (yValue
.size() - 1) / 2) + rect
.height() / 2, xyTextPaint
);int length
= xValue
.size() / 12;if (length
> 0) {for (int j
= 1; j
<= length
; j
++) {float x1
= xInit
+ interval
* j
* 12;if (x1
> xOri
+ interval
* 6)canvas
.drawText(text
, 0, text
.length(), x1
- interval
- rect
.width() - dpToPx(3), yOri
- yLength
* (1.0f * (yValue
.size() - 1) / 2) + rect
.height() / 2, xyTextPaint
);}}}xyTextPaint
);
繪制正常綠色區域(120-160心率)
RectF f
= new RectF();f
.top
= yOri
- yLength
* (4 + 1.0f / 3);f
.bottom
= yOri
- yLength
* 3;f
.left
= xOri
;f
.right
= width
;canvas
.drawRect(f
, okAreaPaint
);
繪制折線圖
private void drawBrokenLine(Canvas canvas
) {if (value
.size() < 2) return;linePaint
.setStyle(Paint
.Style
.STROKE
);linePaint
.setColor(linecolor
);Path path
= new Path();float x
= xInit
- interval
+ interval
* (betweemTime
+ base
) / perLengTime
;float y
= yOri
- yOri
* (1 - 0.1f) * (value
.get(0) - yValue
.get(0)) / (yValue
.get(yValue
.size() - 1) - yValue
.get(0));path
.moveTo(x
, y
);for (int i
= 1; i
< value
.size(); i
+= 3) {x
= getXLength(i
);y
= getYLength(i
);if (isContinuous
) {setContinuousPath(i
, path
, x
, y
);} else {setNoContinuousPath(i
, path
, x
, y
);}}linePaint
.setStrokeWidth(dpToPx(1f));canvas
.drawPath(path
, linePaint
);}
最后一個心率動態打點
private void drawSelectPointLinePoint(Canvas canvas
) {if (value
.size() == 0) return;int position
= value
.size() - 1;float dp7
= dpToPx(5);float x
= xInit
- interval
+ 1.0f * interval
* ((position
+ 1) * betweemTime
+ base
) / perLengTime
;float y
= yOri
- yOri
* (1 - 0.1f) * (value
.get(position
) - yValue
.get(0)) / (yValue
.get(yValue
.size() - 1) - yValue
.get(0));linePaint
.setStyle(Paint
.Style
.FILL
);linePaint
.setColor(markColor
);linePaint
.setStrokeWidth(dpToPx(1));canvas
.drawCircle(x
, y
, dp7
, linePaint
);linePaint
.setStrokeWidth(dpToPx(2));canvas
.drawLine(x
, 0, x
, getHeight(), linePaint
);}
三.處理滑動事件和點擊事件
根據當前手勢滑動的距離來動態更新繪制心率圖,根據手指按下和松開的xy方向的距離來判斷點擊事件(這個可以進行橫豎屏切換)具體代碼如下:
private float startX
, x1
, y1
, x2
, y2
;boolean isTouch
;boolean isClick
;private boolean isScrolling
= false;@Overridepublic boolean onTouchEvent(MotionEvent event
) {if (isScrolling
)return super.onTouchEvent(event
);this.getParent().requestDisallowInterceptTouchEvent(true);obtainVelocityTracker(event
);switch (event
.getAction()) {case MotionEvent
.ACTION_DOWN
:startX
= event
.getX();isClick
= true;x1
= event
.getX();y1
= event
.getY();break;case MotionEvent
.ACTION_MOVE
:if (interval
* xValue
.size() > width
- xOri
) {float dis
= event
.getX() - startX
;startX
= event
.getX();if (xInit
+ dis
< minXInit
) {xInit
= minXInit
;} else if (xInit
+ dis
> maxXInit
) {xInit
= maxXInit
;} else {xInit
= xInit
+ dis
;}invalidate();}isClick
= false;break;case MotionEvent
.ACTION_UP
:if (event
.getX() + getLeft() < getRight() && event
.getY() + getTop() < getBottom()) {x2
= event
.getX();y2
= event
.getY();if (Math
.abs(x1
- x2
) < 6 && Math
.abs(y1
- y2
) < 6) {if (onChartClickListener
!= null
)onChartClickListener
.onChartClick();return false;}}scrollAfterActionUp();this.getParent().requestDisallowInterceptTouchEvent(false);recycleVelocityTracker();break;case MotionEvent
.ACTION_CANCEL
:this.getParent().requestDisallowInterceptTouchEvent(false);recycleVelocityTracker();break;}return true;}
手指抬起后的滑動處理,這個比較耗GPU性能,建議關閉
private void scrollAfterActionUp() {if (!isScroll
)return;final float velocity
= getVelocity();float scrollLength
= maxXInit
- minXInit
;if (Math
.abs(velocity
) < 10000)scrollLength
= (maxXInit
- minXInit
) * Math
.abs(velocity
) / 10000;ValueAnimator animator
= ValueAnimator
.ofFloat(0, scrollLength
);animator
.setDuration((long) (scrollLength
/ (maxXInit
- minXInit
) * 1000));animator
.setInterpolator(new DecelerateInterpolator());animator
.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator valueAnimator
) {float value
= (float) valueAnimator
.getAnimatedValue();if (velocity
< 0 && xInit
> minXInit
) {if (xInit
- value
<= minXInit
)xInit
= minXInit
;elsexInit
= xInit
- value
;} else if (velocity
> 0 && xInit
< maxXInit
) {if (xInit
+ value
>= maxXInit
)xInit
= maxXInit
;elsexInit
= xInit
+ value
;}invalidate();}});animator
.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animator
) {isScrolling
= true;}@Overridepublic void onAnimationEnd(Animator animator
) {isScrolling
= false;}@Overridepublic void onAnimationCancel(Animator animator
) {isScrolling
= false;}@Overridepublic void onAnimationRepeat(Animator animator
) {}});animator
.start();}private float getVelocity() {if (velocityTracker
!= null
) {velocityTracker
.computeCurrentVelocity(1000);return velocityTracker
.getXVelocity();}return 0;}
四 結語
整個心率圖的自定義控件就已經結束了,然后通過設置xy軸的數據,以及具體的折線圖數據就可以顯示一個非常完美的折線圖了。
總結
以上是生活随笔為你收集整理的Android如何自定义一个心电图控件?的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。