[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 更新。

任何问题欢迎评论交流~~~

相关文章
相关标签/搜索