[Android UI] Button的样式实践

4,285 阅读4分钟

背景介绍

Button 是一种非常常用的控件,但是默认的样式基本拿不出手……通常一个 App 的多数按钮都会由 UI 设计一套类似的样式,然后我们可以通过 xml 写的 selector 来定义按钮的具体样式以满足需求。但这样写起来很不舒服,因为当按钮的尺寸变化时,圆角的半径会变化,导致需要写很多 selector 代码。而且 selector 的复用也不算方便,随着业务复杂度提高,按钮的色值也可能出现不止一套,然后就又双叒叕要写 selector 了。此外,这种方式在写 layout 的预览中看不到效果,当 UI 突然想调整细节的时候就要多次 build。

提案

于是产生了两种想法:

  1. 用自定义 View 的方式封装一个 Button,增加需要的 attribute 就可以直接在写 layout 的同时完成按钮的样式的设定。
  2. 写一个工具类,提供设置按钮样式的方法。
方案一:

【优势】

  • 样式代码集中在 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 方法就完成了。直接上代码吧:

第一步:定义属性名称和取值类型

创建attr

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

创建自定义View

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

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

还有动画效果:

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

【效果展示】

xml 中大概这么写:

在 Android Studio 中的预览效果:

运行一下:

BetterButton

抉择

方案一的效果还是很让我满意的,对于一些特殊页面会用到的样式,基本特点是样式不固定而且很少用(比如需要用到图片素材),其实可以考虑使用方案二实现的,或者直接写 selector 也可以接受吧。

后记

一直以来,我都是为了写博客而准备内容,像完成作业一样,虽然内容并不丰富,需要的时间却很长,很不合理。于是有了这篇,从工作中提取的内容。没有多少技术点,而且都在我的能力范围内,写起来轻松了很多~所以也可能有错误和不合适的地方,欢迎评论讨论。

欢迎评论探讨更合理的实现方法。本文相关代码的 github 地址:https://github.com/moqi-Git/Android-widget ,后续做自定义 View 还会在这里更新,可以 star 一下 🤖🤖🤖