Android Paint绘制动态心电图效果
此前自定義View中用的比較多的是對view位置的控制和功能性的融合,視覺上和動畫上的使用要少一些,因此最近心血來潮準備用原生的view繪制些動畫效果出來。
自定義View基礎流程
這里就懶得去查資料了,大致靠記憶寫一下,依次是 onCreate->onMeasure->onLayout->onDraw。
View分類
首先,我們常見的View無非兩種,一種是View(獨立的控件,不能存在子控件),一種是ViewGroup(大多是充當容器的作用,可以包含子空間,例如:XXLayout)。但其實從本質上來說所有View都是繼承于View,包括ViewGroup也僅僅是種“可以包含view的View”,可能看上去比較拗口,誰讓我是一個偏科的工科男,當然是選擇原諒我啦o(>﹏<)o。
構造方法
View的構造方法通常根據傳入參數個數不同會有三個,分別是Context context, AttributeSet attrs, int defStyleAttr。
- Context是基礎,只要和調用系統參數有有一絲絲聯系的地方肯定會有它。
- AttributeSet用于XML文件解析,可以理解成在布局XML中調用才會有。
- defStyleAttr用于解析XML文件中的自定義參數。
測量
onMeasure用來向父容器聲明自己的大小,這個方法很重要也很常用,大致就是告訴父容器自己有多大,是以什么屬性進行放置,會在父容器的onLayout中參與計算。
定位
onLayout用于設置子View的Layout位置,因此,只有ViewGroup才會執行這個方法。
繪制
onDraw中對View進行具體的繪畫操作,決定View最終的展示效果。
draw(Canvas canvas)`,會傳入一個Canvas對象,該對象即是整個View畫布,所以我們需要畫任何效果都是通過canvas.drawXXX()來實現的,這里以繪制一條動態顯示的心電圖為例進行講解。
心電圖實現
設計
心電圖屬于一種不規則的線條圖形,其實就是由多條線段組成而線又可以由點組成,因此實現方式有很多種,最簡單的是通過Canvas.drawPath(Path,Paint)進行繪制。
drawPath
drawPath用于路徑的繪制,路徑也就是由多個點連接而成的不規則圖形,可以相交成閉合的多邊形,也可以不相交成為一段線段,該方法有兩個參數(Path,Paint)。
- Path,用于記錄和保存路徑的坐標集合,path.moveTo(x,y)設置Path的起始點坐標,path.lineTo(x,y)設置Path下個點的坐標并且使用直線將前后兩點相連。心電圖因為都是直線所以使用這兩個方法就夠了,除了這兩個方法還有別的可以畫曲線的這里就不詳細介紹了。
Paint
Paint,畫筆對象,面向對象編程有個特點也是優點,那就是十分貼近我們的生活,既然我們已經有了Canvas畫布,那么按照現實生活中的邏輯,我們就還需要用筆在畫布上進行繪畫才能真正顯示出圖像,因此我們就需要生成一個Paint對象。
其常見的方法如下:
-
setColor(int color); 設置顏色(為畫筆設置一種純色,這個沒什么好說的)
-
setFlags(int flags); 預設一些屬性,如ANTI_ALIAS_FLAG抗鋸齒。
-
setStyle(Style style); 設置畫筆填充類型,共三種:FILL填充,STROKE描邊,FILL_AND_STROKE填充并描邊。如果設置填充則畫出來的圖形是實心的,**不僅僅是針對封閉圖形,線段同樣有效。**相反,設置描邊則僅畫出外邊框即輪廓。
-
setStrokeWidth(float witdh);設置畫筆寬度(粗細)。
-
setAntiAlias(boolean aa); 設置抗鋸齒,和setFlags(ANTI_ALIAS_FLAG)效果相同。
-
setDither(boolean d); 設置防抖動,開啟后會讓圖像顏色顯示更加平滑,和抗鋸齒一樣會效果更多的性能,同樣也可以通過setFlag設置。
-
setShadowLayer(float radius, float dx, float dy, int shadowColor); 設置陰影,第一個參數為陰影效果因數,越大效果越明顯,0則沒有,而且設置0還會報錯(Excuse me?),第二第三個參數為陰影的偏移量,第三個是陰影的顏色。
-
setShader(Shader shader); 設置著色器,可以看成setColor的豪華升級版。通過這個方法可以設置出很多炫酷的色彩效果。
Shader
BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY); 顧名思義,它是將bitmap作為畫筆顏色,需要傳入三個對象,第一個不用說,后面兩個分別是X軸和Y軸的處理模式。一共有三種模式:CLAMP、MIRROR和REPETA。
- CLAMP 其官方解釋為replicate the edge color if the shader draws outside of its original bounds。就是如果著色區域超出了我們設置的Bitmap區域則將Bitmap邊緣最后的像素的顏色作為超出區域的顏色。
- MIRROR 其官方解釋為repeat the shader’s image horizontally and vertically。著色區域超出了我們設置的Bitmap區域則將Bitmap鏡像翻轉之后進行著色。
- REPETA 其官方解釋為repeat the shader’s image horizontally and vertically, alternating mirror images so that adjacent images always seam。和Mirror類似,不過不會進行鏡像處理,而是不停地重復排列。
Gradient
- LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, TileMode tile); 線性漸變,這個是一種比較簡單的漸變著色器。參數依次表示顏色起點坐標、顏色結束坐標、起始顏色、結束顏色、處理模式。除了可以設置兩種顏色漸變,還有另外一個重載構造方法可以設置多種顏色漸變這里就不展示了。起點坐標和結束坐標決定了顏色從什么位置開始漸變,并且根據漸變長度不同,漸變的程度也隨著變化。處理模式和BitmapShader類似。
- SweepGradient(); 梯度漸變,也稱之為掃描式漸變,因為其效果有點類似雷達的掃描效果。
- RadialGradient(); 徑向漸變,徑向漸變說的簡單點就是個圓形中心向四周漸變的效果。
- ComposeShader(); 混合漸變,顧名思義可以將多個著色效果混合,需要傳入三個參數,前兩個為需要混合的著色器對象,最后一個為混合模式。和OpenGL中的Blend類似,有興趣自己去玩玩兒,這里也不具體介紹。
Effect
setPathEffect(PathEffect effect); 從名字上可以看出是專門給Path設置效果的一個方法,(具體是不是只針對Path有效沒具體驗證過- -!)。
- CornerPathEffect(float radius); 將路徑的轉角變得圓滑。
- DiscretePathEffect(float segmentLength, float deviation); 離散路徑效果,其會在路徑上繪制很多“雜點”的突出來模擬一種類似生銹鐵絲的效果。第一個參數指定這些突出的“雜點”的密度,值越小雜點越密集,第二個參數則是“雜點”突出的大小,值越大突出的距離越大反之越小。
- DashPathEffect(float intervals[], float phase); 間斷的路徑效果,將一條連續的路徑轉換成一條類似虛線的間斷線。第一個參數為間斷長度的數組,數組的第一個值為實線長度,第二個為虛線,第三個為實線以此類推。第二個參數為間斷效果的偏移量,通過不斷的修改該值可以達到線段在動的動畫效果。
- PathDashPathEffect(Path shape, float advance, float phase, Style style); 和DashPathEffect類似,不過前者始終是以線的表現形式,而PathDashPathEffect可以通過傳入Path的不同,定義間斷出來的形狀如圓點,方塊等等。
- ComposePathEffect(PathEffect outerpe, PathEffect innerpe); 將兩個效果混合,會先將路徑變成innerpe的效果,再去復合outerpe的路徑效果,即:outerpe(innerpe(Path));
- SumPathEffect(PathEffect first, PathEffect second); 和ComposePathEffect類似,會把兩種路徑效果加起來再作用于路徑。
通過上面的介紹,大致流程是這樣的:
先通過drawPath畫出心電圖類似的上下折線圖形。
然后設置Paint的Shader屬性,通過LinearGradient為Paint添加一個帶有透明的線性漸變的特效。
最后通過一個線程循環定時改變線性顏色開始和結束的偏移量達到動態繪制心電圖的效果。
同時為了提升顯示效果可以為Paint設置一些別的輔助效果和參數。
接下來就是 ShowTime :
下面直接上代碼:
public class MyView extends View {private Paint mPaint;private int mWindowWidth;private int mWindowHeight;private int mOffset;private Handler mHandler = new Handler();public MyView(Context context) {this(context, null);}public MyView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MyView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mOffset = 0;mPaint = new Paint();//設置空心mPaint.setStyle(Paint.Style.FILL_AND_STROKE);//設置線寬mPaint.setStrokeWidth(15f);//設置抗鋸齒mPaint.setAntiAlias(true);//設置防抖動mPaint.setDither(true);//設置陰影 // mPaint.setShadowLayer(25f, 5f, 10f, Color.BLACK);//初始化漸變顏色,因為要達到真正的透明效果,所以使用兩個透明漸變到紅色final int[] colors = new int[]{Color.argb(0, 0, 0, 0), Color.argb(0, 0, 0, 0), Color.RED};//啟用一根新線程進行定時刷新new Thread(new Runnable() {@Overridepublic void run() {while (true) {mOffset += 5;//添加離散效果,讓線條變得更加曲折DiscretePathEffect discretePathEffect = new DiscretePathEffect(3f, 5f);//添加轉角圓滑CornerPathEffect cornerPathEffect = new CornerPathEffect(90f);//設置組合PathEffectmPaint.setPathEffect(new ComposePathEffect(cornerPathEffect, discretePathEffect));//設置線性漸變mPaint.setShader(new LinearGradient(mOffset, 0, 800 + mOffset, 15f, colors, null, Shader.TileMode.REPEAT));//刷新視圖mHandler.post(new Runnable() {@Overridepublic void run() {invalidate();}});try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}}}).start();//獲得屏幕尺寸DisplayMetrics displayMetrics = new DisplayMetrics();((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);mWindowWidth = displayMetrics.widthPixels;mWindowHeight = displayMetrics.heightPixels;}@Overridepublic void draw(Canvas canvas) {super.draw(canvas);//設置折線路徑Path path = new Path();//起始路徑path.moveTo(0, mWindowHeight / 2);//經過路徑path.lineTo(50, mWindowHeight / 2 + 50);path.lineTo(100, mWindowHeight / 2 - 50);path.lineTo(150, mWindowHeight / 2 + 100);path.lineTo(200, mWindowHeight / 2 - 100);path.lineTo(250, mWindowHeight / 2 + 150);path.lineTo(300, mWindowHeight / 2 - 150);path.lineTo(350, mWindowHeight / 2 + 150);path.lineTo(400, mWindowHeight / 2 - 150);path.lineTo(450, mWindowHeight / 2 + 150);path.lineTo(500, mWindowHeight / 2 - 150);path.lineTo(550, mWindowHeight / 2 + 100);path.lineTo(600, mWindowHeight / 2 - 100);path.lineTo(650, mWindowHeight / 2 + 50);path.lineTo(700, mWindowHeight / 2 - 50);path.lineTo(mWindowWidth, mWindowHeight / 2);//drawPathcanvas.drawPath(path, mPaint);} }總結
以上是生活随笔為你收集整理的Android Paint绘制动态心电图效果的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 快速中值滤波在心电图ECG中的应用
- 下一篇: Android集成腾讯X5浏览器内核库