作者簡介 原創(chuàng)微信公眾號郭霖 WeChat ID: guolin_blog
本篇是猴菇先生的第二篇投稿,高度還原了小米時鐘,效果很不錯,希望大家喜歡。
猴菇先生的博客地址:
http://blog.csdn.net/qq_31715429
正文
繼續(xù)練習(xí)自定義View。畢竟熟才能生巧。一直覺得小米的時鐘很精美,那這次就搞它~這次除了練習(xí)自定義View,還涉及到使用 Camera 和 Matrix 實現(xiàn)3D效果。
一個這樣的效果,在繪制的時候最好選擇一個方向一步一步的繪制,這里我選擇由外到內(nèi)、由深到淺的方向來繪制,代碼步驟如下:
1.首先老一套~新建 attrs.xml 文件,編寫自定義屬性如時鐘背景色、亮色(用于分針、秒針、漸變終止色)、暗色(圓弧、刻度線、時針、漸變起始色),新建 MiClockView 繼承 View,重寫構(gòu)造方法,獲取自定義屬性值,初始化 Paint、Path 以及 畫圓、弧需要的RectF 等東東,重寫 onMeasure 計算寬高,這里不再啰嗦~
2.由于 onSizeChanged 方法在構(gòu)造方法、onMeasure 之后,又在 onDraw 之前,此時已經(jīng)完成全局變量初始化,也得到了控件的寬高,所以可以在這個方法中確定一些與寬高有關(guān)的數(shù)值,比如這個 View 的 半徑啊、padding值 等,方便繪制的時候計算大小和位置:
3.準備工作做的差不多了,那就開始繪制,根據(jù)方向我先確定最外層的小時時間文本的位置及其旁邊的四個?。?/p>
注意兩位數(shù)字的寬度和一位數(shù)的寬度是不一樣的,在計算的時候一定要注意:
我計算文本的寬高一般采用的方法是,new一個 Rect,然后再繪制時調(diào)用:
mTextPaint.getTextBounds(timeText,0, timeText.length(), mTextRect);
將這個文本的范圍賦值給這個 mTextRect,此時 mTextRect.width() 就是這段文本的寬,mTextRect.height() 就是這段文本的高。
畫文本旁邊的四個?。?/p>
計算圓弧外接矩形的范圍別忘了加上圓弧線寬的一半。
4.再往里是刻度盤,畫這個刻度盤的思路是現(xiàn)在底層畫一個 mScaleLength 寬度的圓,并設(shè)置 SweepGradient 漸變,上面再畫一圈背景色的刻度線。獲得 SweepGradient 的 Matrix 對象,通過不斷旋轉(zhuǎn) mGradientMatrix 的角度實現(xiàn)刻度盤的旋轉(zhuǎn)效果:
這里有一個全局變量mSecondDegree,即秒針旋轉(zhuǎn)的角度,需要根據(jù)當(dāng)前時間動態(tài)獲?。?/p>
5.然后就是畫秒針,用 Path 繪制一個指向 12點鐘 的三角形,通過不斷旋轉(zhuǎn)畫布實現(xiàn)秒針的旋轉(zhuǎn):
6.看實現(xiàn)圖,時針在分針之下并且比分針顏色淺,那我就先畫時針,仍然是 Path,并且針頭為圓弧狀,那么就用二階貝賽爾曲線,路徑為 moveTo( A),lineTo(B),quadTo(C,D),lineTo(E),close.
7.然后是分針,按照時針的思路:
8.最后由于path是close的,所以干脆畫兩個圓蓋在上面:
9.終于畫完了,onDraw 部分就是這樣:
繪制的時候,尤其是像這樣圓形view,靈活運用:
這一套組合拳可以減少不少三角函數(shù)、角度弧度相關(guān)的計算。
10.辣么接下來就是如何實現(xiàn)觸摸使鐘表3D旋轉(zhuǎn)
借助 Camera類 和 Matrix類,在構(gòu)造方法中:
MatrixmCameraMatrix=newMatrix();
CameramCamera=newCamera();
這段代碼除了 camera 的旋轉(zhuǎn)、平移、縮放之類的操作之外,剩下的代碼一般是固定的.
全局變量 mCameraRotateX 和 mCameraRotateY 應(yīng)該與此時手指觸摸坐標相關(guān)聯(lián)動態(tài)獲取:
解釋一下 camera 旋轉(zhuǎn)角度為啥介么算:
floatrotateX=-(event.getY()-getHeight()/2);
floatrotateY=(event.getX()-getWidth()/2);
是這樣的,當(dāng) camer.rotateX(x) 的 x為正時,圖像繞X軸上半部分向里下半部分向外旋轉(zhuǎn),也就是手指觸摸點就要往上移。這個 x 就會與 event.getY() 的值有關(guān),x越大,繞X軸旋轉(zhuǎn)角度越大,以圓心為原點,往上 event.getY() - getHeight() / 2 的值為負,故 float rotateX = -(event.getY() - getHeight() / 2);
而對于 camer.rotateY(y) 的y為正時,圖像繞Y軸右半部分向里左半部分向外旋轉(zhuǎn),也就是手指觸摸點就要往右移。這個 y 就會與 event.getX() 的值有關(guān),y越大,繞Y軸旋轉(zhuǎn)角度越大,以圓心為原點,往上 event.getX() - getWidth() / 2 的值為正,故 float rotateY = event.getX() - getWidth() / 2。
其他情況大家可以試一下,百度一下 camera 的坐標以及它的旋轉(zhuǎn)是怎么轉(zhuǎn)的~
11.最后在 onTouchEvent 中松開手指時加一個復(fù)原并晃動的動畫
終于寫完了,這個 MiClockView 適配也做的差不多了,時間也是同步的手機時間,一般可以拿來就用了~
后來
額,經(jīng)過我的細心觀察。。發(fā)現(xiàn)撥動時鐘時,時針、分針、秒針和刻度盤會有一個較小的偏移量,形成有層次的、近大遠小的立體偏移效果。。本來打算用 matrix 和 camera 的 mCamera.translate(x, y, z) 方法改變 z 的值,隨著z值增大,原先計算好的大小只會變小,并不會層疊偏移。。所以就隨著手指移動動態(tài)計算位移距離,然后在 onDraw() 的繪制不同零件的方法中不斷 mCanvas.translate(x, y) 達到類似立體偏移的效果。
源碼地址:
https://github.com/MonkeyMushroom/MiClockView
完。。。。。。。。。。。。。。。。。。。。。
文章原創(chuàng)作者GuoLin 書籍推薦
郭林大神原創(chuàng)android 書籍:《第一行代碼 android》
