尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
HarmonyOS7 ArkUI 实战:Button 类型、主题色、Loading 与禁用状态全解析
文章目录前言代码讲解页面入口怎么安排数据模型先稳住状态变量控制页面反馈布局代码不要从样式开始读Builder 和方法承担复用使用方式完整代码关键代码解析interface 不是摆设State 是交互的开关列表和卡片要靠数据驱动事件回调要短一点样式参数别急着抽常量总结前言按钮组件很多项目都会随手写但 loading、禁用和主题切换这些细节决定体验。我建议把这篇当成一个可以直接改造的 ArkUI 页面模板来看。先跑起来确认页面展示和交互都正常再把里面的模拟数据替换成接口数据最后再调整颜色、间距和业务字段。这样改起来比较稳不容易一边改样式一边把状态逻辑弄乱。这篇文章会按「页面意图、数据结构、状态流转、布局组织、交互细节」这条线来读代码。你不用从第一行样式硬看到最后先抓住主线后面的细节会轻松很多。代码讲解页面入口怎么安排代码里的入口组件是ButtonStatesShowcasePage。如果你把它当成单独页面预览保留Entry就可以如果要塞进现有工程的路由体系通常会去掉Entry再由外层页面或路由模块统一管理。这个习惯很重要。一个项目里入口页面太多预览时容易混后期拆组件也会麻烦。我的做法是案例阶段保留入口接业务时只保留真正需要被路由访问的页面。数据模型先稳住本案例涉及的数据模型主要有ButtonDemo、Theme_kolk。这些interface的作用不是为了“看起来规范”而是给 UI 层划边界。比如列表项需要标题、描述、颜色、选中态就应该在模型里说清楚。后续从接口拿数据时可以在请求层做字段映射别让后端字段名直接污染页面代码。如果你准备接真实业务建议把模拟数组替换成接口返回后的 ViewModel。页面只关心自己要渲染什么不关心接口原始字段长什么样。状态变量控制页面反馈本案例里的关键状态包括isLoading、loadingText、clickCount、currentTheme。State的职责是让 UI 对用户操作有反馈。比如选中某个卡片、切换分类、展开内容、修改输入值、控制加载中状态这些都应该由状态驱动。状态一变相关 UI 自动刷新这也是 ArkUI 声明式开发最核心的体验。这里有个容易踩的坑不要把所有数据都塞进State。静态配置用普通数组就够了只有用户会改、接口会更新、页面需要重新渲染的数据才适合放进状态里。布局代码不要从样式开始读这个页面里比较关键的组件有Row横向排列、左右分栏、按钮组或卡片行都依赖它来控制水平方向的节奏。Column纵向组织标题、内容、操作区适合搭页面主骨架。Scroll用于长内容容器锚点跳转和返回顶部都离不开滚动控制。Button按钮案例要关注 loading、disabled、主题色和点击反馈。读布局时我更建议先找大容器再看内部如何拆块。比如一个页面通常会先用Column纵向放标题区、内容区、操作区内容区里再用Row、Grid、List或其他容器细分。这样看代码层级会比逐行看.padding()、.fontSize()清楚得多。如果页面显示错位优先检查容器关系而不是马上改像素。很多问题不是某个.margin()不对而是外层容器的宽高、滚动方向、权重分配或对齐方式没想清楚。Builder 和方法承担复用这篇没有额外拆出 Builder页面结构主要集中在build()中。辅助方法主要有getCurrentThemeColor、doLoadingAction。Builder更适合复用 UI 片段比如卡片、列表行、头部区域、按钮组。普通方法更适合放计算逻辑比如筛选列表、统计数量、切换状态、生成颜色。两者分清楚后页面会干净很多。我不建议把所有东西都塞进build()。刚开始代码少还行需求一多build()会变成几百行后面改一个按钮都要找半天。使用方式把下面的完整代码放到 ArkTS 页面文件里即可运行。文章中的组件名已经是语义化命名不包含编号式案例名称接路由时直接使用ButtonStatesShowcasePage就行。如果你要改成业务页面优先改三处数据模型、状态变量、事件方法。样式可以后调先保证数据流和交互是通的。完整代码// ButtonStatesShowcasePage: 按钮组件全解析 - Button 各种类型与状态 // 知识点: Button、ButtonType、stateEffect、loading状态、图文混排按钮 interface ButtonDemo { demoId: number demoTitle: string demoDesc: string } interface Theme_kolk { key: string, label: string, color: string, textColor: string } Entry Component struct ButtonStatesShowcasePage { State isLoading: boolean false State loadingText: string 提交 State clickCount: number 0 State currentTheme: string primary private themes:Theme_kolk[] [ { key: primary, label: 主要, color: #007DFF, textColor: #FFFFFF }, { key: success, label: 成功, color: #10B981, textColor: #FFFFFF }, { key: warning, label: 警告, color: #F59E0B, textColor: #FFFFFF }, { key: danger, label: 危险, color: #EF4444, textColor: #FFFFFF }, { key: info, label: 信息, color: #6366F1, textColor: #FFFFFF }, ] getCurrentThemeColor(): string { return this.themes.find(t t.key this.currentTheme)?.color ?? #007DFF } doLoadingAction() { if (this.isLoading) return this.isLoading true this.loadingText 提交中... setTimeout(() { this.isLoading false this.loadingText 提交成功 ✓ setTimeout(() { this.loadingText 提交 }, 2000) }, 2000) } build() { Column({ space: 0 }) { // 顶部 Row({ space: 0 }) { Text(Button 按钮组件) .fontSize(18) .fontWeight(FontWeight.Bold) .fontColor(#1A1A1A) } .width(100%) .height(52) .padding({ left: 16, right: 16 }) .backgroundColor(#FFFFFF) .border({ width: { bottom: 1 }, color: #F0F0F0 }) Scroll() { Column({ space: 12 }) { // 一、ButtonType 类型 Column({ space: 10 }) { Text(一、ButtonType 按钮形状) .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor(#1A1A1A) .width(100%) Row({ space: 12 }) { Button(Normal) .type(ButtonType.Normal) .fontSize(14) .backgroundColor(#007DFF) .borderRadius(8) .layoutWeight(1) .height(44) Button(Capsule) .type(ButtonType.Capsule) .fontSize(14) .backgroundColor(#007DFF) .layoutWeight(1) .height(44) Button(Circle) .type(ButtonType.Circle) .fontSize(14) .backgroundColor(#007DFF) .width(44) .height(44) } .width(100%) } .width(100%) .padding(16) .backgroundColor(#FFFFFF) .borderRadius(12) // 二、按钮尺寸 Column({ space: 10 }) { Text(二、按钮尺寸与填充样式) .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor(#1A1A1A) .width(100%) // 大号 Button(大号按钮) .type(ButtonType.Capsule) .width(100%) .height(54) .fontSize(17) .fontWeight(FontWeight.Bold) .backgroundColor(#007DFF) // 中号 Row({ space: 12 }) { Button(中号按钮) .type(ButtonType.Capsule) .layoutWeight(1) .height(44) .fontSize(15) .backgroundColor(#007DFF) Button(中号按钮) .type(ButtonType.Capsule) .layoutWeight(1) .height(44) .fontSize(15) .backgroundColor(#FFFFFF) .fontColor(#007DFF) .border({ width: 1, color: #007DFF }) } .width(100%) // 小号 Row({ space: 8 }) { Button(小号) .type(ButtonType.Capsule) .height(32) .fontSize(13) .padding({ left: 14, right: 14 }) .backgroundColor(#007DFF) Button(小号) .type(ButtonType.Capsule) .height(32) .fontSize(13) .padding({ left: 14, right: 14 }) .backgroundColor(#FFFFFF) .fontColor(#007DFF) .border({ width: 1, color: #007DFF }) Button(小号) .type(ButtonType.Capsule) .height(32) .fontSize(13) .padding({ left: 14, right: 14 }) .backgroundColor(#F0F0F0) .fontColor(#666666) } } .width(100%) .padding(16) .backgroundColor(#FFFFFF) .borderRadius(12) // 三、主题色按钮 Column({ space: 10 }) { Text(三、主题色切换) .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor(#1A1A1A) .width(100%) Row({ space: 8 }) { ForEach(this.themes, (theme: Theme_kolk) { Text(theme.label) .fontSize(13) .fontColor(this.currentTheme theme.key ? #FFFFFF : theme.color) .fontWeight(FontWeight.Medium) .padding({ left: 12, right: 12, top: 6, bottom: 6 }) .backgroundColor(this.currentTheme theme.key ? theme.color : transparent) .border({ width: 1, color: theme.color }) .borderRadius(16) .onClick(() { this.currentTheme theme.key }) }, (theme: Theme_kolk) theme.key) } Button(当前主题按钮) .type(ButtonType.Capsule) .width(100%) .height(50) .fontSize(15) .fontWeight(FontWeight.Bold) .backgroundColor(this.getCurrentThemeColor()) .animation({ duration: 300, curve: Curve.EaseOut }) } .width(100%) .padding(16) .backgroundColor(#FFFFFF) .borderRadius(12) // 四、图标按钮 Column({ space: 10 }) { Text(四、图标 文字按钮) .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor(#1A1A1A) .width(100%) Row({ space: 12 }) { Button() { Row({ space: 6 }) { Text().fontSize(16) Text(搜索).fontSize(14).fontColor(#FFFFFF) } } .type(ButtonType.Capsule) .backgroundColor(#007DFF) .height(44) .layoutWeight(1) Button() { Row({ space: 6 }) { Text().fontSize(16) Text(分享).fontSize(14).fontColor(#10B981) } } .type(ButtonType.Capsule) .backgroundColor(#FFFFFF) .height(44) .border({ width: 1, color: #10B981 }) .layoutWeight(1) } .width(100%) Row({ space: 10 }) { ForEach([❤️, ⭐, , ↗️, ], (emoji: string) { Button() { Column({ space: 2 }) { Text(emoji).fontSize(20) } } .type(ButtonType.Circle) .width(48) .height(48) .backgroundColor(#F5F5F5) }, (emoji: string) emoji) } .width(100%) } .width(100%) .padding(16) .backgroundColor(#FFFFFF) .borderRadius(12) // 五、Loading 加载状态 Column({ space: 10 }) { Text(五、Loading 加载状态) .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor(#1A1A1A) .width(100%) Button(this.loadingText) .type(ButtonType.Capsule) .width(100%) .height(50) .fontSize(16) .backgroundColor(this.isLoading ? #BBBBBB : (this.loadingText.includes(✓) ? #10B981 : #007DFF)) .stateEffect(!this.isLoading) .onClick(() { this.doLoadingAction() }) Text(点击按钮模拟2秒加载状态加载期间禁止重复点击) .fontSize(12) .fontColor(#AAAAAA) .width(100%) } .width(100%) .padding(16) .backgroundColor(#FFFFFF) .borderRadius(12) // 六、禁用状态 Column({ space: 10 }) { Text(六、禁用状态对比) .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor(#1A1A1A) .width(100%) Row({ space: 12 }) { Button(正常状态) .type(ButtonType.Capsule) .layoutWeight(1) .height(44) .fontSize(14) .backgroundColor(#007DFF) .enabled(true) Button(禁用状态) .type(ButtonType.Capsule) .layoutWeight(1) .height(44) .fontSize(14) .backgroundColor(#007DFF) .enabled(false) .opacity(0.4) } .width(100%) Text(已点击次数${this.clickCount}) .fontSize(13) .fontColor(#888888) .width(100%) Button() { Row({ space: 6 }) { Text(➕).fontSize(16) Text(计数 1).fontSize(14).fontColor(#FFFFFF) } } .type(ButtonType.Capsule) .width(100%) .height(44) .backgroundColor(#007DFF) .onClick(() { this.clickCount }) } .width(100%) .padding(16) .backgroundColor(#FFFFFF) .borderRadius(12) } .padding({ left: 12, right: 12, top: 12, bottom: 24 }) } .scrollBar(BarState.Off) .layoutWeight(1) } .width(100%) .height(100%) .backgroundColor(#F5F6FA) } }关键代码解析interface不是摆设很多新手写 ArkUI 页面时会直接在ForEach里使用一堆临时对象。页面小的时候没问题但字段一多就很难知道每个字段到底用于哪里。本案例把数据结构提前声明出来读代码时能很快判断每个 UI 区块依赖哪些字段。后续如果接接口也建议保留这层结构。接口可以变页面模型尽量稳定。这样页面不会因为后端多返回一个字段、少返回一个字段就跟着大改。State是交互的开关本案例的交互反馈都围绕状态展开。点击、切换、输入、刷新这类行为本质上都是修改某个状态值然后让 UI 重新计算显示结果。写这类代码时有一个简单判断如果某个值变化后页面应该立刻变那它大概率应该是状态如果它只是配置项、静态文案、固定颜色就不要放进状态。列表和卡片要靠数据驱动只要页面里出现重复结构就应该优先想到数据数组加ForEach。这样新增一项、删除一项、调整顺序都只动数据不用复制粘贴一段 UI。这个案例的完整代码里已经把主要内容抽成数组或方法。你可以试着多加一条数据看页面是否能自然渲染出来。如果能说明结构是健康的如果加一条就要改很多 UI说明组件拆分还不够。事件回调要短一点.onClick()、onChange()这类回调里可以直接改状态但不建议堆太多逻辑。简单切换可以写在回调里复杂逻辑最好拆成方法。这样做有两个好处一是读页面结构时不会被业务逻辑打断二是后面要加埋点、请求接口、异常处理时有明确的位置可以改。样式参数别急着抽常量这个案例里有不少颜色、间距、圆角、字号。学习阶段直接写在组件链上反而更容易看出效果。等你确定页面风格稳定后再把主题色、通用间距、卡片圆角提到统一常量里。过早抽象会让示例代码变绕。先让页面清楚再谈工程化这是我比较推荐的顺序。总结这个案例真正值得学的不只是某个 ArkUI 组件怎么写而是一个页面如何从数据走到状态再走到布局和交互。你可以按这个顺序改造先替换数据模型再确认状态更新是否正确接着调整布局容器最后打磨样式细节。只要这条主线不乱页面规模变大也不会失控。如果后续继续扩展我会优先把可复用的卡片、列表行、筛选栏或控制区拆成独立组件。这样主页面只保留数据和流程代码会更接近真实项目里的写法。
RELATED

相关推荐

GEO内容优化策略:从语义结构到知识图谱的技术路径

GEO内容优化策略:从语义结构到知识图谱的技术路径

GEO内容优化策略:从语义结构到知识图谱的技术路径 传统SEO关注关键词布局和外链建设,而GEO内容优化策略的技术重心在于语义结构化和知识图谱关联。这种转变源于生成式引擎对内容的理解方式发生了根本性变化。内容优化不再面向排名算法,而是面…

📅 2026/9/13 1:39:20
【华为OD技术面试手撕真题】161、不相交的线 | 手撕真题+思路参考+代码解析(C  C++  Java  Python  JS)(0ms)

【华为OD技术面试手撕真题】161、不相交的线 | 手撕真题+思路参考+代码解析(C C++ Java Python JS)(0ms)

文章目录 一、题目 🎃题目描述 🎃样例1 二、代码参考 🎈C语言思路 🎉C语言代码 🎈C++语言思路 🎉C++代码 🎈Java语言思路 🎉Java代码 🎈Python语言思路 🎉Python代码 🎈JS语言思路 🎉JS代码 作者:KJ.JK 🍂个人博客首页: KJ.JK 🍂专栏介绍: 本…

📅 2026/8/24 2:34:20
TVA具身智能范式研究进展(18)

TVA具身智能范式研究进展(18)

前沿技术探索:AI智能体视觉(TVA,Transformer-based Vision Agent)是依托Transformer架构与“因式智能体”理论所构建的颠覆性工业视觉技术,是集深度强化学习(DRL)、卷积神经网络(CNN…

📅 2026/9/9 21:25:58
MORE NEWS

更多资讯

📰

大模型算力怎么规划?DeepSeek、GLM-5、MiniMax2.5实测对比与本地部署指南

最近这几天,DeepSeek、GLM-5、MiniMax2.5 几乎在同一时间节点放出更新,朋友圈和开发者群里一下子就热闹起来了。我刚把三个模型都跑了一遍,实话实说,无论是对话质量、推理速度还是工具调用的稳定性,这几家的进步都很明…

📰

LSTM与注意力机制融合的时间序列预测实践

1. 项目概述:LSTM与注意力机制在时间序列预测中的融合时间序列预测一直是数据分析领域的核心挑战之一。传统方法在处理长期依赖关系时往往表现不佳,而长短期记忆网络(LSTM)因其独特的门控机制,能够有效捕捉时间序列中的长期依赖模式。近年来&…

📰

YOLOv8森林火灾烟雾检测全流程:数据训练、界面部署与误报抑制

简介:基于YOLOv8的森林火灾早期烟雾预警项目,面向计算机视觉、深度学习方向的毕设与课程设计场景,适合计科、人工智能、自动化等专业学生作为完整实践参考。包内集成源码、可视化界面、完整数据集和部署说明,一并提供核心指标曲线…

📰

Oracle数据库安全评估工具oscanner使用指南

1. oscanner工具概述oscanner是一款专为Oracle数据库设计的开源安全评估框架,采用Java语言开发。作为Kali Linux渗透测试工具集中的重要组件,它通过插件化架构实现了对Oracle数据库的多维度安全检查。我在实际渗透测试工作中发现,很多企业Ora…

📰

UHF RFID Java通信实战:TCP二进制协议与NIO高并发解析

简介:本资源是面向Java开发者与物联网初学者的UHF超高频RFID设备开发实践Demo,聚焦自动化仓储、物流追踪等典型应用场景,解决RFID硬件通信、多标签并发读取及EPC数据解析等核心开发难题。压缩包共81个文件,含5个主功能Java源码&am…

📰

nuclei-templates 实战教程:3 条命令跑通你的第一次漏洞扫描

nuclei-templates 实战教程:3 条命令跑通你的第一次漏洞扫描 【免费下载链接】nuclei-templates Community curated list of templates for the nuclei engine to find security vulnerabilities. 项目地址: https://gitcode.com/GitHub_Trending/nu/nuclei-templ…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬