很多筒子覺得自定義View是高手的象征,其實不然。大家覺得自定義View難很多情況下可能是因為自定義View涉及到了太多的類和API,把人搞得暈乎乎的,那麼今天我們就從最簡單的繪圖API開始,帶大家來一步一步深入自定義View的世界。
先來看看我們今天要實作的一個效果圖:

整個效果很簡單,就是在螢幕上顯示一個鐘表,該鐘表可以自動走動。
OK,那就開始動工吧。
1.準備工作
首先,要實作這個時鐘,我得繼承自View來自己繪制時鐘,因為這種效果沒有辦法繼承已有控件去完善功能。然後我們來看看我們這裡需要哪些變量?在這篇部落格中我暫時不打算介紹自定義屬性以及View的測量,這裡我隻想介紹繪圖API,是以View的大小以及鐘表表針的顔色等我都暫時先給一個固定的值。OK,那麼我們需要的變量主要就是下面幾個:
一共就是這麼多個變量。
2.關于構造方法
大家看到,當我繼承View之後,系統要求我實作它的構造方法,構造方法主要有四個,如下:
1.
該構造方法是當我在Java代碼中new一個View的時候調用的。
2.
該構造方法是當我在布局檔案中添加一個View時調用的。
3.
很多筒子看到第三個參數defStyleAttr之後,誤以為如果我在布局檔案中寫了style就會調用該構造方法,其實不然,這個構造方法系統并不會自己去調用(大家有興趣可以自己寫一個style,然後在這個方法中列印日志,看看該方法究竟會不會調用),要由我們自己顯式調用(可以在第二個構造方法中調用)。那麼這裡的defStyleAttr究竟是什麼意思呢?正如這個參數的字面意思,它是我們為自定義的View指定的一個預設樣式。(後面部落格我們再來詳細說一下這個方法)。
另外,還有一個高版本使用的構造方法,我們這裡暫不做介紹。
一般情況下,我們需要在構造方法中完成一些初始化的操作,比如讀取在XML檔案中定義的屬性,或者初始化畫筆等等,因為我們的控件既有可能是通過Java代碼執行個體化的,也有可能是在布局檔案中通過xml添加的。如前所述,如果我們在Java代碼中初始化控件,那麼将調用第一個構造方法,如果我們在xml布局檔案中添加控件,那麼将調用第二個構造方法。這時問題來了,那我們的初始化控件的方法應該寫在那個構造方法中呢?你可以按下面這種方式來寫:
在兩個構造方法中分别調用初始化的方法,這種方式沒有問題,但是顯得你的思路沒有條理,參考Android提供的其他控件的源碼,我建議在初始化控件時按照下面這種方式來寫:
雖然結果都一樣,但是下面這種寫法顯得你思路很清晰。
3.初始化控件
我們在準備工作中定義了許多變量,包括鐘表的顔色,指針的顔色等等許多變量,那麼接下來我們需要在initView這個方法中來初始化這些變量,以供下一步使用,OK,我們來看一看初始化代碼:
首先是獲得一個目前時間的執行個體,因為我需要根據手機上的時間來顯示鐘表的時間,其次就是對表針的各種屬性和畫筆進行初始化,這裡的東西都很簡單,我就不再一一細說,大家看代碼注釋,相信都能看得懂。
4.繪制鐘表
上面所有的工作做完之後,接下來就是繪制鐘表了,繪制工作我們放在了onDraw方法中執行,在自定義控件時,如果該控件是我們繼承自View來實作的,那麼基本上這個控件就是需要我們自己來繪制了。
OK,那我們來看看鐘表的繪制吧。
鐘表不算複雜,但是我們也需要一步一步來:
首先是繪制表盤,這個最簡單:
radius表示表盤的半徑,通過drawCircle繪制一個圓環,四個參數分别是圓環的中心點坐标,圓環的半徑以及繪制圓環的畫筆。
圓環畫好之後,那麼接下來就是繪制表心了,也就是表盤正中心那個紅色的圓心。
很簡單吧。
OK,兩個最簡單的東東畫完之後,那麼接下來就是繪制表盤的時間刻度了,時間刻度的繪制除了數字之外,還有一個綠色的短線,我們一共要畫十二個這種東西,那麼這個要怎麼繪制呢?思路有很多,你可以按照高中的數學知識,計算出每一個數字的坐标以及每一個短線起始位置和結束位置的坐标,然後繪制出來,毫無疑問,這種方式的計算量太大,那我們這裡采取一個簡單的方式:我每次隻在十二點的那個位置上進行繪制,如果需要繪制一點,那麼我把畫布逆時針旋轉30度到十二點的位置,然後畫上1和一個短線之後再将畫布順時針旋轉30度,如果是繪制2點,那麼我把畫布逆時針旋轉60度到十二點的位置,然後繪制上2和一個短線,繪制完成之後再将畫布順時針旋轉60度,思路就是這樣,下面我們來看看代碼:
我用一個循環來繪制這十二個刻度,在每次旋轉畫布之前,我都通過一個canvas.save()方法來儲存畫布目前的狀态,儲存之後再對畫布進行旋轉操作,旋轉完成之後就是畫線畫數字,這些都很簡單,在繪制完成之後,我需要調用canvas的restore()方法,該方法可以讓畫布恢複到旋轉之前的角度。一般情況下,canvas.save()方法和canvas.restore()方法都是成對出現的,這一點大家要注意。
OK,這些東西都繪制完成之後,接下來就該繪制表針了,表針的繪制思路和上面一樣,也是先旋轉表盤,然後繪制表針,繪制完成之後,再把表盤旋轉回之前的狀态。這裡我就不再詳細說明了,大家看代碼:
OK,所有的事情做完之後,我們可以在布局檔案中添加如下代碼,來檢視我們的工作做得怎麼樣:
添加完成之後,運作,不出意外的話,你的App上應該已經顯示了一個時鐘了,但是這個時鐘是靜止的,那麼我們該怎麼讓時鐘動起來呢?其實很簡答,我們隻需要每隔一秒重新擷取calendar執行個體,然後重繪鐘表即可,是以,在onDraw方法的開始和結束,我們還要分别添加如下兩行代碼:
1.開始處添加:
這行代碼用來擷取最新的時間的執行個體
2.結束處添加:
這行代碼用來重繪鐘表,不過重繪是在1秒之後。
OK,至此,我們的自定義鐘表就完成了,完整的代碼應該是這個樣子:
以上。