[Android UI] 自定義 View 練習——TaggedSeekBar

一直沒有本身梳理一遍 View 體系的知識,之前寫自定義 View 一涉及細節就全靠 Google。最近在 deepin 下搞了一份 AOSP 項目,準備從源碼中再學習一遍自定義 View 的寫法。在開始以前,先寫了一個簡單的自定義 View,用來複習自定義 View 的流程。git

TaggedSeekBar 在 SeekBar 的基礎上加了一個顯示進度值的標籤,也能夠當作 ProgressBar 使用,最終效果以下(壓縮的比較慘,重在領會精神):github

知識點

TaggedSeekBar 直接繼承自 View,雖然比較簡陋,但確實包含了自定義 View 的大部分知識點。好比:api

  1. View 的座標體系
  2. Paint 基本屬性
  3. Canvas 基本繪製函數
  4. onMeasure 測量自身
  5. onTouchEvent 處理用戶交互

詳情就不展開說了,有不少大佬都專門寫過的。bash

流程

寫自定義 View 時,最重要的第一步是「拆」。良好的拆解可使 xml 中的參數更易理解,也能夠簡化 onDraw 中的繪製座標計算過程。函數

我把 TaggedSeekBar 拆分紅三部分:progress bar,thumb 和 tag(tag 分爲箭頭和本體),詳情如圖(看我充滿靈魂的手繪):工具

拆分以知足需求爲主,儘可能保證良好的可擴展性。拆完以後就能夠開始編碼了學習

1. xml屬性配置

當多個自定義 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>
複製代碼

2. 讀取屬性,配置默認值

xml 配置參數的缺點是沒法限制屬性關聯,每一個屬性均可能沒寫,因此獲取屬性的時候都須要填入默認值。編碼

設置的默認值須要考慮到 View 元素相關性,儘可能少用固定值,這樣能儘量下降上手難度。spa

3. 測量和定位

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()
複製代碼

4. 分層繪製

onDraw中後繪製的內容會覆蓋在上面,這決定了座標計算的順序。TaggedSeekBar 中的繪製順序是:進度條底色->進度條->thumb->tag

1.進度條底色(圓角矩形)

2.進度條(圓角矩形,覆蓋在底色上)

3.Thumb(圓形)

4.tagIndicator(三角形)

繪製 api 不包含的圖形能夠用 Path,複用以前須要 reset。

5.tag(圓角矩形)

5. 響應 Touch 事件

事件的處理從響應 ACTION_DOWN開始,拿到 event 的座標以後首先要肯定點擊位置是否支持拖動。

若是不支持就當無事發生,支持的話就開始處理ACTION_MOVE並重繪自身。

關於 Listener 的配置仍是以知足需求爲主,這裏只添加了兩種:一是隨拖動實時回調進度,二是鬆手後回調一次。

Tips

在編碼和寫博客期間遇到了一些小問題,順便記錄一下。

1. attrs.xml 裏 name 衝突

以前沒太注意,通常都是換個名字對付過去了。正確解法應該是這樣的: 【代碼】

2. 真機錄製 gif

用模擬器運行代碼的時候可使用 LICEcap 直接錄製 gif,在手機上運行就稍微複雜了點,adb 不支持錄製 gif,能夠採起錄製視頻再轉爲 gif 的方式。轉換工具推薦 ffmpeg 命令,一行代碼搞定:

  • -i | 輸入文件
  • -vf scale=360:-1 |轉換的同時縮放尺寸,寬高比爲 360:-1,-1 表示保持比例自適應高度

剛接觸 ffmpeg,感受用處不少,研究以後單獨寫一下吧。

完整代碼以及 sample 項目能夠在【Github 連接】查看。

轉眼間也用了一年 Kotlin 了,UI 相關的內容也快一年沒寫過博客了,重整了一下這個項目,近期還有別的自定義 View 更新。

任何問題歡迎評論交流~~~

相關文章
相關標籤/搜索