[Android UI] 自定义 View 练习——TaggedSeekBar

一直没有自己梳理一遍 View 体系的知识,以前写自定义 View 一涉及细节就全靠 Google。最近在 deepin 下搞了一份 AOSP 项目,准备从源码中再学习一遍自定义 View 的写法。在开始之前,先写了一个简单的自定义 View,用来复习自定义 View 的流程。

TaggedSeekBar 在 SeekBar 的基础上加了一个显示进度值的标签,也可以当做 ProgressBar 使用,最终效果如下(压缩的比较惨,重在领会精神):

知识点

TaggedSeekBar 直接继承自 View,虽然比较简陋,但确实包含了自定义 View 的大部分知识点。比如:

  1. View 的坐标体系
  2. Paint 基本属性
  3. Canvas 基本绘制函数
  4. onMeasure 测量自身
  5. onTouchEvent 处理用户交互

详情就不展开说了,有很多大佬都专门写过的。

流程

写自定义 View 时,最重要的第一步是「拆」。良好的拆解可以使 xml 中的参数更易理解,也可以简化 onDraw 中的绘制坐标计算过程。

我把 TaggedSeekBar 拆分成三部分:progress bar,thumb 和 tag(tag 分为箭头和本体),详情如图(看我充满灵魂的手绘):

拆分以满足需求为主,尽量保证良好的可扩展性。拆完之后就可以开始编码了

1. xml属性配置

当多个自定义 attr 的 name 冲突时,可以将 attr 的定义提取到外层,结构是这样的:

<?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 元素相关性,尽量少用固定值,这样能尽可能降低上手难度。

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

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

转载于:https://juejin.im/post/5cdd40f4f265da039a3d964d

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值