CircularProgressButton:会变形为圆形进度的按钮
登录、上传、支付这类操作有个经典体验诉求——按下按钮后,按钮原地变成加载动画,成功变绿、失败变红,整个过程不跳转、不弹窗。手写这套"按钮 ↔ 圆形进度条变形 + 状态切换"其实很繁琐(要处理形变动画、状态机、进度环绘制)。dmytrodanylyk 的 CircularProgressButton 是这方面的经典 Android 库:一个 progress 值就能驱动四种状态(Idle / Progress / Complete / Error),并自动完成方形 ↔ 圆形的 morph 动画。注意它已被作者后续的 android-morphing-button 取代,但"状态机 + morph"的设计至今仍是这类交互的范本。参考 GitHub 仓库。
1、概述:它是什么
官方一句话:"Android Button which can morph to Circular Progress"(能在按钮和圆形进度之间变形的 Android 按钮)。它的核心不是"加个进度条",而是用状态机统一管理按钮的所有形态,状态切换伴随平滑的 morph 动画。
定位 按钮 ↔ 圆形进度条 的变形按钮
核心机制 状态机:一个 progress 值驱动四种状态
四状态 Idle(0) / Progress(1-99) / Complete(100) / Error(-1)
视觉效果 方形 ↔ 圆形 平滑 morph 变形动画
包名 com.dd.CircularProgressButton
状态 2014 年经典库,已被作者后续的 android-morphing-button 取代核心要点:
1. 它是"状态机驱动的变形按钮",不是普通带进度的按钮
2. 状态切换(方形→圆形→完成/错误)有自动 morph 动画,无需手写
3. 一个 setProgress(int) 方法 + 一个 progress 值,控全部状态
4. 诚实提示:作者已用 android-morphing-button 作为更新替代,但本项目的设计思路仍值得学习与复用
2、状态机:progress 值驱动四状态(核心)
这是整个库的灵魂。按钮的形态完全由一个 progress 整数值决定——这是它最优雅的设计:
progress 值 状态 形态 典型场景
-----------------------------------------------------------------------
0 Idle 方形按钮(显示文字) 初始 / 可点击
1 ~ 99 Progress 自动 morph 为圆形 + 进度弧 上传/加载中
100 Complete 完成(绿色 / 图标 / 文字) 成功
-1 Error 错误(红色 / 图标 / 文字) 失败
// 关键:setProgress(1) 会自动触发"方形 → 圆形"的 morph 动画
// 这一步是库帮你做的,不用手写形变核心要点:
1. 一个值控四种状态:0=Idle、1-99=Progress、100=Complete、-1=Error
2. setProgress(1) 是分水岭——它触发方形按钮 morph 成圆形进度条
3. 业务代码只需"在合适的时机 setProgress 对应值",形态/动画全自动
4. 这种"状态即数值"的设计,让 UI 与业务逻辑彻底解耦
3、集成与 XML 布局
通过 JitPack 引入(README 方式),再在布局里声明 com.dd.CircularProgressButton,配几条 cpb_ 属性:
// settings.gradle 加 JitPack 仓库
dependencyResolutionManagement {
repositories { ... maven { url 'https://jitpack.io' } }
}
// app/build.gradle
dependencies {
implementation 'com.github.dmytrodanylyk:circular-progress-button:$latest'
// 版本号查 https://jitpack.io/#dmytrodanylyk/circular-progress-button
}<!-- 布局声明:com.dd.CircularProgressButton -->
<com.dd.CircularProgressButton
android:id="@+id/btnWithText"
android:layout_width="196dp"
android:layout_height="64dp"
android:textColor="@color/white"
android:textSize="18sp"
app:cpb_textComplete="@string/Complete"
app:cpb_textError="@string/Error"
app:cpb_textIdle="@string/Upload" />核心要点:
1. JitPack 引入,implementation 'com.github.dmytrodanylyk:circular-progress-button:$latest'
2. 布局用全限定类名 com.dd.CircularProgressButton(注意包名是 com.dd)
3. cpb_textIdle / cpb_textComplete / cpb_textError 分别配三种状态的文字
4. 若用 ProGuard,需 keep com.dd.StrokeGradientDrawable 的 setStrokeColor 方法(见官方 ProGuard 配置)
4、代码控制:setProgress 切换状态
业务里只需在"合适的时机"调用 setProgress,按钮会自动 morph 到对应状态。一个经典登录流程只需四行:
CircularProgressButton btn = findViewById(R.id.btnWithText);
btn.setProgress(0); // Idle:方形按钮,等待点击
btn.setProgress(1); // 进入 Progress:自动 morph 成圆形(进度开始)
btn.setProgress(50); // 进度推进到 50%(圆形弧度增长)
btn.setProgress(100); // Complete:变绿色 + 完成文字 / 图标
// btn.setProgress(-1); // 失败时:变红色 + 错误文字 / 图标
// 典型登录流程伪代码
btn.setOnClickListener(v -> {
btn.setProgress(1); // 1. 点击即进入加载(morph 成圆)
loginApi.login(user, pwd, new Callback() {
public void onSuccess() { btn.setProgress(100); } // 2. 成功变绿
public void onError() { btn.setProgress(-1); } // 3. 失败变红
});
});核心要点:
1. setProgress 是唯一的控制入口,传不同值即切换状态 + 动画
2. setProgress(1) 触发"方形→圆形" morph,是加载开始的信号
3. 成功 setProgress(100)、失败 setProgress(-1),按钮自动变绿/变红
4. 整个登录交互(点击→加载→结果)不弹窗、不跳转,全在按钮原地完成
5、Indeterminate 不确定进度模式
很多场景(如登录)不知道"进度到哪了",只想让进度环无限旋转表示"处理中"。这时用 Indeterminate 模式:
// 开启不确定进度模式
btn.setIndeterminateProgressMode(true);
// 此时 0 < progress < 100 都显示"无限旋转"(而不是停在某个百分比)
btn.setProgress(50); // 圆环开始无限旋转
// 处理完成后用 100 / -1 表示成功 / 失败
btn.setProgress(100); // 完成
btn.setProgress(0); // 切回 Idle 正常状态
// 区别:
// 普通模式: setProgress(50) = 显示 50% 的固定圆弧
// Indeterminate:setProgress(50) = 圆环无限旋转(不知何时完成)核心要点:
1. setIndeterminateProgressMode(true) 切换到不确定进度模式
2. 该模式下 setProgress(1~99) 显示无限旋转,适合"不知进度"的请求
3. 100/-1 仍表示完成/错误,setProgress(0) 切回 Idle
4. 登录、提交等"只有成功/失败两态"的场景,几乎都该用 Indeterminate
6、外观定制:颜色、圆角、图标
通过 app:cpb_ 前缀的属性定制每种状态的外观。颜色类属性用 res/color 下的 color state selector,配合 cpb_selectorXxx 指向:
属性 作用
------------------------------------------------------------------------
cpb_textIdle/Complete/Error 三种状态的文字
cpb_selectorIdle/Complete/Error 三种状态的背景颜色(指向 color selector)
cpb_colorIndicator 进度环颜色(圆形时的弧)
cpb_colorIndicatorBackground 进度环背景(圆形的底环)
cpb_colorProgress 圆形按钮的填充色
cpb_cornerRadius 方形按钮的圆角
cpb_iconComplete/Error 完成/错误状态的图标<!-- res/color/idle_state_selector.xml:用 color selector 控按压/可用色 -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="@color/cpb_blue_dark"/>
<item android:state_enabled="false" android:color="@color/cpb_grey"/>
<item android:state_enabled="true" android:color="@color/cpb_blue"/>
</selector>
<!-- 布局里用 cpb_selectorIdle 指向它 -->
<com.dd.CircularProgressButton
...
app:cpb_selectorIdle="@color/idle_state_selector"
app:cpb_cornerRadius="48dp"
app:cpb_iconComplete="@drawable/ic_action_accept" />核心要点:
1. 每种状态可单独配文字(cpb_textXxx)和颜色(cpb_selectorXxx)
2. 颜色用 res/color 下的 color state selector(不是 drawable),支持按压态
3. cpb_colorIndicator / colorIndicatorBackground / colorProgress 控进度环三层颜色
4. cpb_iconComplete / cpb_iconError 让成功/失败显示图标,比文字更直观
7、原理:源码结构与 morph 机制
读它的源码(library/src/main/java/com/dd/)能学到不少自定义 View 技巧。它把"状态管理、变形动画、进度绘制"拆成了几个职责清晰的类:
类 职责
------------------------------------------------------------------------
CircularProgressButton 主 View,状态机入口,setProgress 在此
MorphingAnimation 方形 ↔ 圆形 的变形动画(宽高/圆角插值)
CircularProgressDrawable 确定进度的圆弧绘制(1~99%)
CircularAnimatedDrawable 不确定进度的无限旋转动画
StrokeGradientDrawable 带描边的渐变 Drawable(ProGuard 要 keep setStrokeColor)
StateManager 状态管理(Idle/Progress/Complete/Error 切换)
OnAnimationEndListener 动画结束回调核心要点(设计精髓):
1. MorphingAnimation 是视觉灵魂——对按钮的"宽度/高度/圆角"做属性动画插值,实现方形→圆形的平滑变形
2. 进度绘制与变形分离:MorphingAnimation 管"变形",CircularProgressDrawable 管"画弧"
3. 确定 vs 不确定用两个 Drawable:ProgressDrawable 画固定弧,AnimatedDrawable 画旋转动画
4. StateManager 把状态切换收敛到一处,setProgress 只负责"告诉它目标状态"
8、总结
CircularProgressButton 的核心洞察是:把"按钮的多种形态"收敛成一个由 progress 值驱动的状态机,再让变形动画全自动。业务代码只管"现在该是哪个状态",UI 怎么变交给库。
关键要点:
- 定位:按钮 ↔ 圆形进度条 变形按钮,2014 年经典 Android 库
- 状态机:progress 值 0(Idle)/1-99(Progress)/100(Complete)/-1(Error) 驱动
- 控制:setProgress(int) 一个入口;setIndeterminateProgressMode 切不确定模式
- 定制:cpb_ 前缀属性配文字/颜色 selector/圆角/图标
- 原理:MorphingAnimation 管变形、XxxDrawable 管画弧、StateManager 管状态
- 提示:已停更,被作者后续的 android-morphing-button 取代,思路仍值得学习
对于做登录、上传、支付这类"原地加载→原地反馈结果"交互的开发者而言,CircularProgressButton"状态机 + morph 变形"的设计范式至今仍是教科书级的参考——哪怕最终不用这个库(它已停更),把"一个值驱动多状态 + 变形动画分离 + 状态管理收敛"这套思路用到自己的自定义 View 里,就能用很少的代码做出同样流畅的体验。想要更新的实现,可以转向作者后续的 android-morphing-button;想理解"按钮变形进度条"为什么优雅,回到这个库读它的源码,依然会有收获。


