ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

鸿蒙清新卡片式审美:ArkUI 声明式布局搭出待办清单的统计与输入面板

2026/8/16 4:38:11 拓冰建站 浏览量
鸿蒙清新卡片式审美:ArkUI 声明式布局搭出待办清单的统计与输入面板


实例:待办事项清单(Todo List)|风格:清新卡片式

一、整体布局结构

页面采用「统计面板 → 输入区 → 状态切换 → 列表 → 底部操作栏」五段式结构,让面板内容丰富但不拥挤:

┌────────────────────────────────────┐ │ ✅ 待办清单 共 15 项 │ ← 标题栏 ├────────────────────────────────────┤ │ ▢ 未完成 12 ✓ 已完成 3 ⚡高优 4 │ ← 统计卡(3 个数字块) ├────────────────────────────────────┤ │ [输入框: 输入新待办…] [分类] [新增] │ ← 输入区 ├────────────────────────────────────┤ │ [全部] [未完成] [已完成] │ ← 状态切换(分段控件) ├────────────────────────────────────┤ │ ┌──────────────────────────────┐ │ │ │ ⬜ 完成周报提交 工作 ⚡ │ │ ← 待办卡片(多张) │ │ 今天 09:30 │ │ │ │ [改] [完成] [删] │ │ │ └──────────────────────────────┘ │ │ ... │ ├────────────────────────────────────┤ │ 已完成 3 项 ▏▏▏▏▏▏▏▏▏▏ 20% 清空 │ ← 底部进度条 + 清空已完成 └────────────────────────────────────┘

二、各区块说明

1. 标题栏

  • 左侧:✅ 待办清单标题 + 副标题「优先级排序 · 未完成在前」
  • 右侧:刷新按钮(),点击重新查询

2. 统计卡(3 个数字块)

数据项数据来源展示样式
未完成statistics().pending大号蓝色数字
已完成statistics().done大号绿色数字
高优先级statistics().high大号橙色数字

卡片为圆角白底、左右三块均分,数字下方有小字标签。

3. 输入区

组件说明
TextInput待办内容输入,占位符「输入新待办,回车或点新增…」
Select(分类)下拉选择:工作 / 生活 / 学习 / 日常
优先级选择三选一按钮:低 · 中 · 高
新增按钮主色填充圆角按钮「+ 新增」

4. 状态切换分段控件

三个 Tab:全部 / 未完成 / 已完成,切换后调用queryByCompleted()刷新列表,同时统计卡联动变化。

5. 待办卡片(列表主体)

每张卡片包含:

元素样式
完成勾选圈未完成:空心圆;已完成:绿色实心圆 + 白勾
标题未完成黑色加粗;已完成灰色 + 删除线
分类标签小圆角底色标签(工作蓝/生活绿/学习紫/日常灰)
优先级高优显示 ⚡ 橙色角标
时间灰色小字「MM-DD HH:mm」
操作按钮「改」打开编辑弹窗、「完成/取消完成」、「删除」

已完成卡片整体置灰、透明度 0.6,视觉上弱化。

6. 底部进度条

  • 完成率进度条:已完成/总数百分比
  • 右侧「清空已完成」按钮(红色小字),一键删除全部已完成项

三、UI 风格要素

风格项取值
背景色#F5F7FA浅灰蓝
卡片底色白色,圆角 12,浅阴影
主色#3B82F6蓝(按钮/选中态)
成功色#22C55E绿(完成态)
警告色#F97316橙(高优先级)
删除色#EF4444
字体默认 HarmonyOS Sans

四、弹窗交互

  • 编辑弹窗:点击「改」弹出底部弹窗,可修改内容、分类、优先级、备注,点「保存」调用update()
  • 删除确认:点「删除」弹出 AlertDialog 二次确认,避免误删
  • 清空确认:清空已完成前同样弹窗确认

五、空态与加载

场景展示
无任何待办居中插画表情 + 「暂无待办,输入上方内容开始吧」
切换分类后无数据「该分类下暂无待办」提示条
加载中列表顶部显示细进度条

六、文章小结

本篇设计了五段式页面结构:统计面板让顶部信息量充足,分段控件提供三种视图,卡片承载待办内容,底部进度条呈现完成率。整体「清新卡片式」风格统一、信息层级清晰,且每个数字、每个标签都直接对应 DAO 查询结果,为后续文章的数据填充做好了 UI 容器准备。


七、页面信息架构与状态管理详解

1. @Entry 与 @Component:入口与组件的边界

在 ArkTS 中,@Entry标记的 struct 是页面级入口,可被router.pushUrl()跳转加载;@Component则声明一个可复用的 UI 组件。本页由三层结构组成:

@Entry// 页面入口:可被路由加载@Componentstruct TodoPage{// 顶层页面容器@StatetodoList:TodoItem[]=[]// 列表数据@Statestatistics:Statistics={pending:0,done:0,high:0}@StatecurrentTab:number=0// 0全部 1未完成 2已完成@StateinputText:string=''// 输入框内容@StateshowLoading:boolean=false// 加载中开关aboutToAppear(){this.refreshAll()// 页面首次出现即查库}...}

2. @State 是声明式刷新的「信号源」

装饰器作用本页应用
@Entry标记页面入口,可被 router 跳转顶层 TodoPage
@Component声明组件,可独立复用TodoPage / TodoCard
@State组件内可变状态,变化即触发 UI 重建todoList、currentTab 等
@Builder复用一段 UI 结构统计数字块、卡片操作按钮

核心思想是只改数据、不碰 DOM:点「完成」后只需this.todoList[i].completed = 1,ArkUI 会自动对比新旧状态树并重绘对应卡片,开发者无需手动刷新或操作节点。

3. 从 DAO 到 UI 的数据流闭环

用户点击(新增/完成/筛选) ↓ 调用 TodoDao.insert() / toggleCompleted() / queryByCompleted() ↓ Promise 返回结果 赋值给 @State 变量(todoList / statistics) ↓ 状态变化 ArkUI 状态树 diff → 只重建受影响的组件

这条链路里,DAO 负责「取数」,@State 负责「存数」,ArkUI 负责「画数」,三者各司其职,页面代码因此保持简洁。

八、四大区块的 UI 实现与样式拆解

1. 标题栏:Flex 两端对齐

Row(){Column(){Text('✅ 待办清单').fontSize(22).fontWeight(FontWeight.Bold)Text('优先级排序 · 未完成在前').fontSize(12).fontColor('#9CA3AF')}.alignItems(HorizontalAlign.Start)Blank()// 弹性占位:把两侧顶开Button('↻').onClick(()=>this.refreshAll())}.width('100%').padding(16)

要点:Blank()是弹性空白组件,自动占据剩余宽度,实现「左标题、右按钮」的经典顶栏;刷新按钮回调refreshAll()重新查库。

2. 统计面板:三等分数字卡

Row(){this.statBlock('未完成',this.statistics.pending,'#3B82F6')this.statBlock('已完成',this.statistics.done,'#22C55E')this.statBlock('高优先级',this.statistics.high,'#F97316')}.padding(12).backgroundColor(Color.White).borderRadius(12).shadow({radius:6,color:'rgba(0,0,0,0.06)'})

三个数字块用@Builder抽成公共方法,保证样式完全一致:

@BuilderstatBlock(label:string,value:number,color:string){Column(){Text(`${value}`).fontSize(28).fontWeight(FontWeight.Bold).fontColor(color)Text(label).fontSize(12).fontColor('#6B7280')}.layoutWeight(1).padding({top:12,bottom:12})}

layoutWeight(1)让三块在 Row 内均分宽度,这是「卡片三等分」的关键,也方便日后增加第四块数字。

3. 输入区:TextInput + Select + 主色按钮

控件关键属性说明
TextInputplaceholder / onChange待办内容输入,双向绑定 inputText
Selectoptions 数组分类下拉:工作/生活/学习/日常
ButtonbackgroundColor 主色「+ 新增」提交并清空输入框
Row({space:8}){TextInput({placeholder:'输入新待办,回车或点新增…',text:this.inputText}).onChange(v=>this.inputText=v).layoutWeight(1)Button('+ 新增').backgroundColor('#3B82F6').onClick(()=>this.addTodo())}.width('100%').padding(12)

4. 列表卡片:状态驱动的行样式

ForEach(this.todoList,(item:TodoItem)=>{TodoCard({item:item,onToggle:()=>this.toggleItem(item.id),onDelete:()=>this.confirmDelete(item.id),onEdit:()=>this.openEditDialog(item)})},(item:TodoItem)=>`${item.id}`)

已完成卡片的置灰、删除线全部由三元表达式驱动,无需写 if/else 分支去操作样式对象:

.opacity(item.completed===1?0.6:1)Text(item.title).decoration({type:item.completed===1?TextDecorationType.LineThrough:TextDecorationType.None}).fontColor(item.completed===1?'#9CA3AF':'#1F2937')

九、状态筛选与交互设计

1. 点击筛选:分段控件联动统计

Tabs({barPosition:BarPosition.Start}){TabContent(){this.listByState(0)}.tabBar('全部')TabContent(){this.listByState(1)}.tabBar('未完成')TabContent(){this.listByState(2)}.tabBar('已完成')}.onChange(asyncindex=>{this.currentTab=indexthis.todoList=awaitTodoDao.queryByCompleted(this.context,index===0?-1:index-1)// -1 表示不过滤this.statistics=awaitTodoDao.statistics(this.context)// 统计联动})

切换 Tab 触发重新查库,列表与统计卡同步刷新,形成「筛选 → 查询 → 渲染」的完整闭环,保证任何视图下数字都真实可信。

2. 删除确认:AlertDialog 二次确认

AlertDialog.show({title:'确认删除',message:'删除后不可恢复,确定删除这条待办吗?',primaryButton:{value:'取消',action:()=>{}},secondaryButton:{value:'删除',fontColor:'#EF4444',action:()=>this.deleteItem(pendingId)}})

删除是破坏性操作,二次确认可避免误触;「清空已完成」按钮同样复用该弹窗,只是 message 改为「将删除全部 N 条已完成待办」。

十、ArkTS 声明式开发的注意事项

1. ForEach 的 key 必须唯一且稳定

key 生成写法是否推荐原因
(item) => item.id✅ 推荐主键唯一、不随渲染变化,增删不错位
(item) => item.title⚠️ 谨慎内容可重复、可编辑,复用会错乱
(item, index) => index❌ 避免增删后 index 漂移,动画与状态全乱

2. 事件绑定传参要用箭头函数

// ❌ 错误:render 时就立即执行了 deleteItem.onClick(this.deleteItem(item.id))// ✅ 正确:箭头函数延迟到点击时才执行.onClick(()=>this.deleteItem(item.id))

3. 状态驱动刷新:改「数据」而非改「样式」

  • 数组整体替换:this.todoList = newList,触发全量 diff(推荐)
  • 单项更新:先splice(i, 1, newItem)整体替换该下标元素
  • 禁止直接改@State数组元素属性后不赋值——ArkUI 无法感知对象内部的细粒度变化

十一、FAQ 常见问题

Q1:统计数字刷新了,列表却没变,为什么?
A:多半是列表用了另一个数组(如临时变量)赋值,或queryByCompleted返回新数组后没有整体赋给todoList。检查 @State 变量是否被真正替换。

Q2:删除卡片后动画错乱、行内容串位?
A:几乎都是 ForEach 的 key 用了 index。改用item.id后,ArkUI 才能正确识别「谁被删、谁保留」。

Q3:@State 里存对象,改了对象属性页面不刷新?
A:需要整体替换对象引用(如this.statistics = { ...this.statistics, done: n }),或将高频变化的字段拆成独立 @State。

Q4:如何让加载进度条只在查询期间显示?
A:用@State showLoading,在 await 查询前置true、拿到结果后置false,配合if (this.showLoading) { LoadingProgress() }条件渲染即可。