Android之玩轉(zhuǎn)View(八):Path

請(qǐng)尊重原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處【tianyl】的博客

關(guān)于的Android之玩轉(zhuǎn)View目錄

前言

在了解了Paint和Canvas之后诉儒,接下來(lái)就來(lái)說(shuō)是Path,我是比較喜歡稱Paint籍救、Canvas和Path為自定義繪制View時(shí)的三劍客邻寿,主要是它們?cè)诶L制我們想要View時(shí),能起到非常重要的作用涮雷。

接下來(lái)阵面,就說(shuō)說(shuō)Path的一些用法

1 Path的基礎(chǔ)用法

對(duì)于Path的基礎(chǔ)操作,我比較喜歡歸納為點(diǎn)操作洪鸭、線操作和圖形操作

1.1 點(diǎn)操作

點(diǎn)操作的api主要有moveTo样刷、rMoveTo,其實(shí)這兩個(gè)方法的作用比較類似览爵,只有細(xì)微的不同

  • moveTo (x,y):移動(dòng)下一個(gè)操作的起始點(diǎn)到坐標(biāo)點(diǎn)(x,y)

例如:對(duì)于一個(gè)Path颂斜,它的默認(rèn)起始點(diǎn)是(0,0),即屏幕左上角拾枣,當(dāng)使用了moveTo(100,100)沃疮,后,它的起始點(diǎn)就變成了坐標(biāo)點(diǎn)(100,100)的位置

  • rMoveTo(x,y):移動(dòng)下一個(gè)操作的起始點(diǎn)到當(dāng)前點(diǎn)的相對(duì)位置的坐標(biāo)(x,y)梅肤,這個(gè)r就是relative的首字母司蔬,它表示相對(duì)位置

例如:對(duì)于一個(gè)Path,它的默認(rèn)起始點(diǎn)是(0,0)姨蝴,即屏幕左上角俊啼,當(dāng)使用了moveTo(100,100),后左医,它的起始點(diǎn)就變成了坐標(biāo)點(diǎn)(100,100)的位置授帕,這個(gè)時(shí)候,如果我們?cè)偈褂胢oveTo(100,100)浮梢,就沒(méi)有任何效果跛十,因?yàn)槲覀兊钠鹗键c(diǎn)已經(jīng)在坐標(biāo)點(diǎn)(100,100)上了,如果用rMoveTo(100,100)秕硝,那么起始點(diǎn)就會(huì)變成(200,200)芥映,這就是rMoveTo的用途了

1.2 線操作

說(shuō)完了點(diǎn)操作,那么線操作也就比較簡(jiǎn)單了,線操作的api也是兩個(gè)lineTo奈偏、rLineTo

  • lineTo (x,y):從起始點(diǎn)(默認(rèn)是坐標(biāo)原點(diǎn)(0,0))到坐標(biāo)點(diǎn)(x,y)繪制一條線

  • rLineTo(x,y):從起始點(diǎn)(默認(rèn)是坐標(biāo)原點(diǎn)(0,0))到相對(duì)坐標(biāo)點(diǎn)(x,y)繪制一條線坞嘀,這個(gè)相對(duì)坐標(biāo)也是當(dāng)前的起始點(diǎn),比如當(dāng)前起始點(diǎn)是坐標(biāo)點(diǎn)(100,100),那么實(shí)際的結(jié)束點(diǎn)就是坐標(biāo)點(diǎn)(100+x,100+y)

除此之外惊来,還有setLastPoint和close兩個(gè)api

  • setLastPoint(dx, dy):改變之前操作的終點(diǎn)的位置

例如:


//初始化Path

Path path = new Path();

//畫從(0,0)到(400,400)之間的直線

path.lineTo(400, 400);

//新加的setLastPoint

path.setLastPoint(100, 800);

path.lineTo(400, 800);

canvas.drawPath(path, mPaint);

這段代碼等價(jià)于


//初始化Path

Path path = new Path();

//畫從(0,0)到(100,400)之間的直線

path.lineTo(100, 800);

path.lineTo(400, 800);

canvas.drawPath(path, mPaint);

關(guān)于對(duì)api setLastPoint的解釋丽涩,我查了一下資料,網(wǎng)上說(shuō)moveTo影響的是后面操作的起點(diǎn)位置裁蚁。不會(huì)影響之前的操作矢渊;而 setLastPoint改變前一步操作最后一個(gè)點(diǎn)的位置,不僅影響前一步操作厘擂,同一時(shí)候也會(huì)影響后一步操作昆淡。其實(shí)我感覺(jué)因?yàn)樗梢灾苯颖恢暗膌ineTo取代,即直接改lineTo的傳遞的參數(shù)即可刽严,所以這個(gè)方法我用得并不多

還有一個(gè)方法

  • clost():封閉當(dāng)前的繪制路徑昂灵,這個(gè)api很簡(jiǎn)單,也算比較常用

1.3 圖形操作

接下來(lái)就是Path的圖形操作了舞萄,也是我們經(jīng)常用來(lái)進(jìn)行圖形繪制的api眨补,它們分別是

  • 繪制圓:addCircle

  • 繪制橢圓:addOval

  • 繪制矩形:addRect

  • 繪制圓角矩形:addRoundRect

對(duì)于這些api,除了它們各自特定的參數(shù)之外倒脓,還會(huì)有一個(gè)相同的參數(shù)Direction

對(duì)于參數(shù)Direction撑螺,它有兩個(gè)選項(xiàng)

  • Direction.CW:按照逆時(shí)針?lè)较蚶L制

  • Direction.CCW:按照順時(shí)針?lè)较蚶L制

對(duì)于這個(gè)參數(shù),在大多數(shù)情況下崎弃,我們使用順時(shí)針繪制和逆時(shí)針繪制都不會(huì)影響最終結(jié)果甘晤,只是在少數(shù)情況下,比如如果使用了setLastPoint這個(gè)方法饲做,那么就會(huì)因?yàn)槔L制方向的不同得到不同的結(jié)果线婚,或者我們需要根據(jù)繪制的方向來(lái)自定義一些動(dòng)畫,或者使用繪制的路徑盆均,這時(shí)也需要考慮我們繪制的方向造成的影像塞弊。

2 貝塞爾曲線

說(shuō)完了Path的基礎(chǔ)api,再說(shuō)說(shuō)Path稍微難一點(diǎn)的用法泪姨,這也是Android中實(shí)現(xiàn)一些比較炫酷特效的方法——貝塞爾曲線游沿。

說(shuō)到貝塞爾曲線,就先簡(jiǎn)單的介紹一下什么是貝塞爾曲線

2.1 介紹

關(guān)于貝塞爾曲線的介紹肮砾,這里參考維基百科

線性曲線

線性貝塞爾曲線函數(shù)中的t會(huì)經(jīng)過(guò)由P0至P1的B(t)所描述的曲線诀黍。例如當(dāng)t=0.25時(shí),B(t)即一條由點(diǎn)P0至P1路徑的四分之一處唇敞。就像由0至1的連續(xù)t蔗草,B(t)描述一條由P0至P1的直線

一階貝塞爾曲線

二次曲線

為建構(gòu)二次貝塞爾曲線咒彤,可以中介點(diǎn)Q0和Q1作為由0至1的t:

由P0至P1的連續(xù)點(diǎn)Q0疆柔,描述一條線性貝塞爾曲線咒精。

由P1至P2的連續(xù)點(diǎn)Q1,描述一條線性貝塞爾曲線旷档。

由Q0至Q1的連續(xù)點(diǎn)B(t)模叙,描述一條二次貝塞爾曲線。

二階貝塞爾曲線

高階曲線

為建構(gòu)高階曲線鞋屈,便需要相應(yīng)更多的中介點(diǎn)范咨。對(duì)于三次曲線,可由線性貝塞爾曲線描述的中介點(diǎn)Q0厂庇、Q1渠啊、Q2,和由二次曲線描述的點(diǎn)R0权旷、R1所建構(gòu)


三階貝塞爾曲線

2.2 Android 中的貝塞爾曲線

介紹了貝塞爾曲線的基礎(chǔ)知識(shí)替蛉,接下來(lái)說(shuō)說(shuō)Android中的貝塞爾曲線,Android系統(tǒng)已經(jīng)封裝好了二次貝塞爾曲線和三次貝塞爾曲線的api拄氯,我們可以直接使用

  • 二次貝塞爾曲線:quadTo()

  • 三次貝塞爾曲線:cubicTo()

這兩個(gè)api就是Android系統(tǒng)提供給我們使用的api躲查,當(dāng)然,如果想要使用更高階的貝塞爾曲線译柏,那么就需要自己去實(shí)現(xiàn)了(一般情況下镣煮,這兩個(gè)api就已經(jīng)夠用了),接下來(lái)我就通過(guò)一個(gè)簡(jiǎn)單水波紋的例子鄙麦,說(shuō)明貝塞爾曲線在Android中的應(yīng)用

2.2.1 通過(guò)貝塞爾曲線實(shí)現(xiàn)水波紋效果

首先典唇,在寫代碼之前,我們要先確定如何通過(guò)一個(gè)貝塞爾曲線繪制出一個(gè)水波紋胯府,首先介衔,我們需要繪制出一個(gè)波,然后我們對(duì)這個(gè)波進(jìn)行移動(dòng)盟劫,那么就是我們想要的動(dòng)畫效果了

如圖所示

設(shè)計(jì)思路

這是我們實(shí)現(xiàn)水波紋的基本思路

  1. 首先我們移動(dòng)到波的起始點(diǎn)(x夜牡,y)

  2. 假設(shè)一個(gè)波的完整長(zhǎng)度是waveLength,波的高度是waveHeight

  3. 首先從波的起始點(diǎn)(x侣签,y)塘装,控制點(diǎn)為1,繪制貝塞爾曲線到中間點(diǎn)(x1影所,y1)蹦肴,然后在使用控制點(diǎn)2,從中間點(diǎn)繪制貝塞爾曲線到結(jié)束點(diǎn)(x2猴娩,y2)

  4. 假設(shè)這個(gè)波的一個(gè)規(guī)則的正弦波阴幌,那么起始點(diǎn)的坐標(biāo)為(0勺阐,height/2),控制點(diǎn)1的坐標(biāo)為(waveLength/4矛双,0)渊抽,中間點(diǎn)的坐標(biāo)為(waveHeight/2,height/2)议忽,控制點(diǎn)2的坐標(biāo)為(3*waveLength/4懒闷,height),結(jié)束點(diǎn)的坐標(biāo)為(waveLength栈幸,height/2)

  5. 封閉繪制路徑

具體代碼如下


Path path = new Path();

//步驟1

path.moveTo(0, height/2);

//步驟3 注意這里使用的是rQuadTo愤估,也就是相對(duì)當(dāng)前的位置

path.rQuadTo(waveLength / 4, -height/2, waveLength / 2,0);

path.rQuadTo(waveLength / 4, height/2, waveLength / 2, 0);

//步驟4

path.lineTo(mWidth, mHeight);

path.lineTo(0, mHeight);

path.close();

canvas.drawPath(path, mPaint);

具體效果如下(為了效果突出,所以設(shè)置的波高較大))

效果圖
  1. 完成了靜態(tài)的波速址,接下來(lái)就只需要將這個(gè)波進(jìn)行平移即可玩焰,之前我們波的起始點(diǎn)是(x,y)芍锚,我們通過(guò)不斷的平移這個(gè)起始點(diǎn)昔园,就可以實(shí)現(xiàn)動(dòng)畫的效果,當(dāng)然為了避免邊界的空白闹炉,我們一般可以從view外的開(kāi)始繪制蒿赢,然后超出view,這樣就通過(guò)一個(gè)貝塞爾曲線實(shí)現(xiàn)了水波紋效果

//這里移動(dòng)到-waveLength是為了超出屏幕外渣触,避免動(dòng)畫的時(shí)候出現(xiàn)空白羡棵,offsetX就是波的偏移量,只要不斷的修改這個(gè)偏移量嗅钻,就能實(shí)現(xiàn)動(dòng)畫效果
path.moveTo(-waveLength + offsetX, mHeight / 2);
//繪制到超出屏幕即可
for (int i = -waveLength; i < getWidth() + waveLength; i += waveLength) {
   path.rQuadTo(waveLength / 4, -height/2, waveLength / 2,0);
   path.rQuadTo(waveLength / 4, height/2, waveLength / 2, 0);
}

下面是添加了動(dòng)畫效果后的完整draw代碼

private void drawWave(Canvas canvas) {
    Path path = new Path();
    path.moveTo(-mWaveWidth + offsetX, mHeight / 2);
    //注意這里使用的是rQuadTo皂冰,也就是相對(duì)當(dāng)前的位置
    for (int i = -mWaveWidth; i < getWidth() + mWaveWidth; i += mWaveWidth) {
        path.rQuadTo(mWaveWidth / 4, -mHeight / 2, mWaveWidth / 2, 0);
        path.rQuadTo(mWaveWidth / 4, mHeight / 2, mWaveWidth / 2, 0);
    }
    path.lineTo(mWaveWidth, mHeight);
    path.lineTo(0, mHeight);
    path.close();
    canvas.drawPath(path, mPaint);
}

效果如下


水波紋
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市养篓,隨后出現(xiàn)的幾起案子秃流,更是在濱河造成了極大的恐慌,老刑警劉巖柳弄,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舶胀,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡碧注,警方通過(guò)查閱死者的電腦和手機(jī)嚣伐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)萍丐,“玉大人轩端,你說(shuō)我怎么就攤上這事∈疟洌” “怎么了基茵?”我有些...
    開(kāi)封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵奋构,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我拱层,道長(zhǎng)弥臼,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任舱呻,我火速辦了婚禮醋火,結(jié)果婚禮上悠汽,老公的妹妹穿的比我還像新娘箱吕。我一直安慰自己,他們只是感情好柿冲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布茬高。 她就那樣靜靜地躺著,像睡著了一般假抄。 火紅的嫁衣襯著肌膚如雪怎栽。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天宿饱,我揣著相機(jī)與錄音熏瞄,去河邊找鬼。 笑死谬以,一個(gè)胖子當(dāng)著我的面吹牛强饮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播为黎,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼邮丰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了铭乾?” 一聲冷哼從身側(cè)響起剪廉,我...
    開(kāi)封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎炕檩,沒(méi)想到半個(gè)月后斗蒋,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡笛质,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年泉沾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片经瓷。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡爆哑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出舆吮,到底是詐尸還是另有隱情揭朝,我是刑警寧澤队贱,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站潭袱,受9級(jí)特大地震影響柱嫌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜屯换,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一编丘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧彤悔,春花似錦嘉抓、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至杨赤,卻和暖如春敞斋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背疾牲。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工植捎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人阳柔。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓焰枢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親盔沫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子医咨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353