背景介绍
Button 是一种非常常用的控件,但是默认的样式基本拿不出手……通常一个 App 的多数按钮都会由 UI 设计一套类似的样式,然后我们可以通过 xml 写的 selector 来定义按钮的具体样式以满足需求。但这样写起来很不舒服,因为当按钮的尺寸变化时,圆角的半径会变化,导致需要写很多 selector 代码。而且 selector 的复用也不算方便,随着业务复杂度提高,按钮的色值也可能出现不止一套,然后就又双叒叕要写 selector 了。此外,这种方式在写 layout 的预览中看不到效果,当 UI 突然想调整细节的时候就要多次 build。
提案
于是产生了两种想法:
- 用自定义 View 的方式封装一个 Button,增加需要的 attribute 就可以直接在写 layout 的同时完成按钮的样式的设定。
- 写一个工具类,提供设置按钮样式的方法。
方案一:
【优势】
- 样式代码集中在 xml 文件中,可以减少 Activity 中的 View 层相关代码
- 能在写 layout 的时候预览
【劣势】
- 不容易扩展和迁移
- 你的同事可能会想不起来用(emmm…这个应该不算问题吧)
方案二:
【优势】
- 扩展很简单…加一个方法就行
- 迁移也很简单,可以都在一个类里
- 写好方法注解就能说服同事一起用吧(这个也是凑数的)
【劣势】
- 不能预览
- 需要把设置样式的代码写在 Activity 中
总之先实现一下
【设计思路】
通过 selector 设置的样式有:
- enable 状态下的背景颜色,边框颜色,边框宽度,文字颜色
- pressed 状态下的背景颜色,边框颜色,边框宽度,文字颜色
- disable 状态下的背景颜色,边框颜色,边框宽度,文字颜色
还有交互上的效果:
- 点击状态下 按钮缩小
- 点击状态下 按钮位移
只要把这些参数提取出来,变成 xml 中的属性标签就好了:
| name | value | meaning |
|---|---|---|
| solid | color | 填充颜色 |
| solidPressed | color | pressed填充颜色 |
| solidDisable | color | disable填充颜色 |
| strokeWidth | dimen | 边框宽 |
| strokeColor | color | 边框颜色 |
| strokeColorPressed | color | pressed边框颜色 |
| strokeColorDisable | color | disable边框颜色 |
| textColorEnable | color | 文字颜色 |
| textColorPressed | color | pressed文字颜色 |
| textColorDisable | color | disable文字颜色 |
| corner | dimen | 圆角半径4个 |
| radiusLeftTop | dimen | 圆角半径左上 |
| radiusLeftBottom | dimen | 圆角半径左下 |
| radiusRightTop | dimen | 圆角半径右上 |
| radiusRightBottom | dimen | 圆角半径右下 |
| pressedAnime | enum | 点击效果动画 |
【编码思路】
在 View 的代码中取得以上属性,设置给按钮,然后根据属性去生成类似 selector 的 Drawable 并设置为 View 的背景。关于点击效果的其他效果则需要修改 onTouch 方法,增加一些基本的动画效果。
动画效果也可以开放更多的参数,比如缩放或平移的数值,动画的 duration 等。因为实际上动画效果不太常用,过多的属性看起来也挺头疼的,就先做成了选择的形式,可以在几种固定的效果里选一个也挺够用的了。代码中会简单说明一下动画相关的参数怎么提取,属性动画相关的可以看我另一篇文章(大概吧):【链接】
【编码流程】
自定义属性 -> 继承 View -> 获取属性值 -> 生成相关参数并设置
比自定义的 View 简单很多,可以直接继承 TextView ,也不用自行处理绘制的方法,只要调用一些 set 方法就完成了。直接上代码吧:
第一步:定义属性名称和取值类型

第二步:继承TextView创建BetterButton,添加属性

第三步:获取xml中的属性值赋给对应属性

第四步:把自定义属性转换为BetterButton的属性并设置给this

还有动画效果:


这里如果重写 onTouchEvent 方法,就必须处理 performClick ,否则就无法响应点击事件。也就是还要区分需要判定为点击的情况,和取消了点击的情况,换成 setOnTouchListener 就可以不处理点击事件,只要在对应的事件触发时添加动画,然后 return false 。
【效果展示】
xml 中大概这么写:

在 Android Studio 中的预览效果:

运行一下:

抉择
方案一的效果还是很让我满意的,对于一些特殊页面会用到的样式,基本特点是样式不固定而且很少用(比如需要用到图片素材),其实可以考虑使用方案二实现的,或者直接写 selector 也可以接受吧。
后记
一直以来,我都是为了写博客而准备内容,像完成作业一样,虽然内容并不丰富,需要的时间却很长,很不合理。于是有了这篇,从工作中提取的内容。没有多少技术点,而且都在我的能力范围内,写起来轻松了很多~所以也可能有错误和不合适的地方,欢迎评论讨论。
欢迎评论探讨更合理的实现方法。本文相关代码的 github 地址:https://github.com/moqi-Git/Android-widget ,后续做自定义 View 还会在这里更新,可以 star 一下 🤖🤖🤖