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