作者簡(jiǎn)介 原創(chuàng)微信公眾號(hào)郭霖 WeChat ID: guolin_blog
本篇是猴菇先生的第二篇投稿忠藤,高度還原了小米時(shí)鐘,效果很不錯(cuò),希望大家喜歡芍殖。
猴菇先生的博客地址:
http://blog.csdn.net/qq_31715429
正文
繼續(xù)練習(xí)自定義View。畢竟熟才能生巧谴蔑。一直覺(jué)得小米的時(shí)鐘很精美豌骏,那這次就搞它~這次除了練習(xí)自定義View,還涉及到使用 Camera 和 Matrix 實(shí)現(xiàn)3D效果隐锭。
一個(gè)這樣的效果窃躲,在繪制的時(shí)候最好選擇一個(gè)方向一步一步的繪制,這里我選擇由外到內(nèi)钦睡、由深到淺的方向來(lái)繪制蒂窒,代碼步驟如下:
1.首先老一套~新建 attrs.xml 文件,編寫(xiě)自定義屬性如時(shí)鐘背景色荞怒、亮色(用于分針洒琢、秒針、漸變終止色)褐桌、暗色(圓弧衰抑、刻度線(xiàn)、時(shí)針荧嵌、漸變起始色)呛踊,新建 MiClockView 繼承 View,重寫(xiě)構(gòu)造方法啦撮,獲取自定義屬性值谭网,初始化 Paint、Path 以及 畫(huà)圓赃春、弧需要的RectF 等東東愉择,重寫(xiě) onMeasure 計(jì)算寬高,這里不再啰嗦~
2.由于 onSizeChanged 方法在構(gòu)造方法、onMeasure 之后薄辅,又在 onDraw 之前要拂,此時(shí)已經(jīng)完成全局變量初始化,也得到了控件的寬高站楚,所以可以在這個(gè)方法中確定一些與寬高有關(guān)的數(shù)值脱惰,比如這個(gè) View 的 半徑啊、padding值 等窿春,方便繪制的時(shí)候計(jì)算大小和位置:
3.準(zhǔn)備工作做的差不多了拉一,那就開(kāi)始繪制,根據(jù)方向我先確定最外層的小時(shí)時(shí)間文本的位置及其旁邊的四個(gè)痪善颉:
注意兩位數(shù)字的寬度和一位數(shù)的寬度是不一樣的蔚润,在計(jì)算的時(shí)候一定要注意:
我計(jì)算文本的寬高一般采用的方法是,new一個(gè) Rect尺栖,然后再繪制時(shí)調(diào)用:
mTextPaint.getTextBounds(timeText,0, timeText.length(), mTextRect);
將這個(gè)文本的范圍賦值給這個(gè) mTextRect嫡纠,此時(shí) mTextRect.width() 就是這段文本的寬,mTextRect.height() 就是這段文本的高延赌。
畫(huà)文本旁邊的四個(gè)怀怠:
計(jì)算圓弧外接矩形的范圍別忘了加上圓弧線(xiàn)寬的一半。
4.再往里是刻度盤(pán)挫以,畫(huà)這個(gè)刻度盤(pán)的思路是現(xiàn)在底層畫(huà)一個(gè) mScaleLength 寬度的圓者蠕,并設(shè)置 SweepGradient 漸變,上面再畫(huà)一圈背景色的刻度線(xiàn)掐松。獲得 SweepGradient 的 Matrix 對(duì)象踱侣,通過(guò)不斷旋轉(zhuǎn) mGradientMatrix 的角度實(shí)現(xiàn)刻度盤(pán)的旋轉(zhuǎn)效果:
這里有一個(gè)全局變量mSecondDegree,即秒針旋轉(zhuǎn)的角度大磺,需要根據(jù)當(dāng)前時(shí)間動(dòng)態(tài)獲嚷站洹:
5.然后就是畫(huà)秒針,用 Path 繪制一個(gè)指向 12點(diǎn)鐘 的三角形杠愧,通過(guò)不斷旋轉(zhuǎn)畫(huà)布實(shí)現(xiàn)秒針的旋轉(zhuǎn):
6.看實(shí)現(xiàn)圖待榔,時(shí)針在分針之下并且比分針顏色淺,那我就先畫(huà)時(shí)針殴蹄,仍然是 Path,并且針頭為圓弧狀猾担,那么就用二階貝賽爾曲線(xiàn)袭灯,路徑為 moveTo( A),lineTo(B),quadTo(C,D),lineTo(E),close.
7.然后是分針,按照時(shí)針的思路:
8.最后由于path是close的绑嘹,所以干脆畫(huà)兩個(gè)圓蓋在上面:
9.終于畫(huà)完了稽荧,onDraw 部分就是這樣:
繪制的時(shí)候,尤其是像這樣圓形view工腋,靈活運(yùn)用:
這一套組合拳可以減少不少三角函數(shù)姨丈、角度弧度相關(guān)的計(jì)算畅卓。
10.辣么接下來(lái)就是如何實(shí)現(xiàn)觸摸使鐘表3D旋轉(zhuǎn)
借助 Camera類(lèi) 和 Matrix類(lèi),在構(gòu)造方法中:
MatrixmCameraMatrix=newMatrix();
CameramCamera=newCamera();
這段代碼除了 camera 的旋轉(zhuǎn)蟋恬、平移翁潘、縮放之類(lèi)的操作之外,剩下的代碼一般是固定的.
全局變量 mCameraRotateX 和 mCameraRotateY 應(yīng)該與此時(shí)手指觸摸坐標(biāo)相關(guān)聯(lián)動(dòng)態(tài)獲燃哒:
解釋一下 camera 旋轉(zhuǎn)角度為啥介么算:
floatrotateX=-(event.getY()-getHeight()/2);
floatrotateY=(event.getX()-getWidth()/2);
是這樣的拜马,當(dāng) camer.rotateX(x) 的 x為正時(shí),圖像繞X軸上半部分向里下半部分向外旋轉(zhuǎn)沐绒,也就是手指觸摸點(diǎn)就要往上移俩莽。這個(gè) x 就會(huì)與 event.getY() 的值有關(guān),x越大乔遮,繞X軸旋轉(zhuǎn)角度越大扮超,以圓心為原點(diǎn),往上 event.getY() - getHeight() / 2 的值為負(fù)蹋肮,故 float rotateX = -(event.getY() - getHeight() / 2)出刷;
而對(duì)于 camer.rotateY(y) 的y為正時(shí),圖像繞Y軸右半部分向里左半部分向外旋轉(zhuǎn)括尸,也就是手指觸摸點(diǎn)就要往右移巷蚪。這個(gè) y 就會(huì)與 event.getX() 的值有關(guān),y越大濒翻,繞Y軸旋轉(zhuǎn)角度越大屁柏,以圓心為原點(diǎn),往上 event.getX() - getWidth() / 2 的值為正有送,故 float rotateY = event.getX() - getWidth() / 2淌喻。
其他情況大家可以試一下,百度一下 camera 的坐標(biāo)以及它的旋轉(zhuǎn)是怎么轉(zhuǎn)的~
11.最后在 onTouchEvent 中松開(kāi)手指時(shí)加一個(gè)復(fù)原并晃動(dòng)的動(dòng)畫(huà)
終于寫(xiě)完了雀摘,這個(gè) MiClockView 適配也做的差不多了裸删,時(shí)間也是同步的手機(jī)時(shí)間,一般可以拿來(lái)就用了~
后來(lái)
額阵赠,經(jīng)過(guò)我的細(xì)心觀(guān)察涯塔。。發(fā)現(xiàn)撥動(dòng)時(shí)鐘時(shí)清蚀,時(shí)針匕荸、分針、秒針和刻度盤(pán)會(huì)有一個(gè)較小的偏移量枷邪,形成有層次的榛搔、近大遠(yuǎn)小的立體偏移效果。。本來(lái)打算用 matrix 和 camera 的 mCamera.translate(x, y, z) 方法改變 z 的值践惑,隨著z值增大腹泌,原先計(jì)算好的大小只會(huì)變小,并不會(huì)層疊偏移尔觉。凉袱。所以就隨著手指移動(dòng)動(dòng)態(tài)計(jì)算位移距離,然后在 onDraw() 的繪制不同零件的方法中不斷 mCanvas.translate(x, y) 達(dá)到類(lèi)似立體偏移的效果穷娱。
源碼地址:
https://github.com/MonkeyMushroom/MiClockView
完绑蔫。。泵额。配深。。嫁盲。篓叶。。羞秤。缸托。。瘾蛋。俐镐。。哺哼。佩抹。。取董。棍苹。。茵汰。
文章原創(chuàng)作者GuoLin 書(shū)籍推薦
郭林大神原創(chuàng)android 書(shū)籍:《第一行代碼 android》