一直沒有本身梳理一遍 View 體系的知識,之前寫自定義 View 一涉及細節就全靠 Google。最近在 deepin 下搞了一份 AOSP 項目,準備從源碼中再學習一遍自定義 View 的寫法。在開始以前,先寫了一個簡單的自定義 View,用來複習自定義 View 的流程。git
TaggedSeekBar 在 SeekBar 的基礎上加了一個顯示進度值的標籤,也能夠當作 ProgressBar 使用,最終效果以下(壓縮的比較慘,重在領會精神):github
TaggedSeekBar 直接繼承自 View,雖然比較簡陋,但確實包含了自定義 View 的大部分知識點。好比:api
詳情就不展開說了,有不少大佬都專門寫過的。bash
寫自定義 View 時,最重要的第一步是「拆」。良好的拆解可使 xml 中的參數更易理解,也能夠簡化 onDraw 中的繪製座標計算過程。函數
我把 TaggedSeekBar 拆分紅三部分:progress bar,thumb 和 tag(tag 分爲箭頭和本體),詳情如圖(看我充滿靈魂的手繪):工具
拆分以知足需求爲主,儘可能保證良好的可擴展性。拆完以後就能夠開始編碼了學習
當多個自定義 attr 的 name 衝突時,能夠將 attr 的定義提取到外層,結構是這樣的:ui
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="progressWidth" format="dimension|reference"/>
<declare-styleable name="TaggedSeekBar">
<attr name="progressWidth"/>
</declare-styleable>
<declare-styleable name="XXProgressBar">
<attr name="progressWidth"/>
</declare-styleable>
</resources>
複製代碼
xml 配置參數的缺點是沒法限制屬性關聯,每一個屬性均可能沒寫,因此獲取屬性的時候都須要填入默認值。編碼
設置的默認值須要考慮到 View 元素相關性,儘可能少用固定值,這樣能儘量下降上手難度。spa
View 的 layout 方式符合咱們的需求,不須要重寫 onLayout。onMeasure 須要重寫來支持 wrap_content
。進度條是水平方向的,因此 width 能多大就取多大,height 能夠 wrap_content
,最小高度只要顯示完整內容就好。
//當height的測量模式是 AT_MOST 時,
//height = tagHeight+ThumbHeight+indicatorHeight
height = paddingBottom + paddingTop + thumbRadius * 2 + thumbStrokeWidth * 2 + indicatorHeight + tagHeight).toInt()
複製代碼
onDraw
中後繪製的內容會覆蓋在上面,這決定了座標計算的順序。TaggedSeekBar 中的繪製順序是:進度條底色->進度條->thumb->tag
1.進度條底色(圓角矩形)
2.進度條(圓角矩形,覆蓋在底色上)
3.Thumb(圓形)
4.tagIndicator(三角形)
繪製 api 不包含的圖形能夠用 Path,複用以前須要 reset。
5.tag(圓角矩形)
事件的處理從響應 ACTION_DOWN
開始,拿到 event 的座標以後首先要肯定點擊位置是否支持拖動。
若是不支持就當無事發生,支持的話就開始處理ACTION_MOVE
並重繪自身。
關於 Listener 的配置仍是以知足需求爲主,這裏只添加了兩種:一是隨拖動實時回調進度,二是鬆手後回調一次。
在編碼和寫博客期間遇到了一些小問題,順便記錄一下。
以前沒太注意,通常都是換個名字對付過去了。正確解法應該是這樣的: 【代碼】
用模擬器運行代碼的時候可使用 LICEcap 直接錄製 gif,在手機上運行就稍微複雜了點,adb 不支持錄製 gif,能夠採起錄製視頻再轉爲 gif 的方式。轉換工具推薦 ffmpeg 命令,一行代碼搞定:
剛接觸 ffmpeg,感受用處不少,研究以後單獨寫一下吧。
完整代碼以及 sample 項目能夠在【Github 連接】查看。
轉眼間也用了一年 Kotlin 了,UI 相關的內容也快一年沒寫過博客了,重整了一下這個項目,近期還有別的自定義 View 更新。
任何問題歡迎評論交流~~~