# HarmonyOS ArkTS 滑动删除列表实战(三):交互式列表增删操作
摘要列表滑动删除是移动应用中极为常见的交互模式。本文详细讲解如何在 HarmonyOS ArkTS 中实现带有切换删除按钮的列表支持添加和删除列表项涵盖 List 组件、手势交互、状态管理和动画反馈等核心技术点。一、项目概述「滑动删除」功能是移动端列表交互的标配——用户在列表项上向左滑动露出删除按钮点击即可删除该项。同时我们也提供「添加」功能让用户可以动态扩充列表。本项目的核心功能点功能描述技术实现列表展示以列表形式展示数据项List ListItem滑动显示删除左滑列表项露出删除按钮手势监听 状态控制删除确认点击删除按钮移除列表项splice 删除 状态更新新增项添加新的列表条目数组 push List 刷新删除按钮切换点击按钮切换为删除模式Toggle 状态驱动二、项目架构entry/src/main/ets/ ├── pages/ │ └── SwipeDelete.ets // 滑动删除主页面 ├── components/ │ ├── SwipeableItem.ets // 可滑动列表项组件 │ └── AddItemDialog.ets // 添加项目的弹窗组件 ├── model/ │ └── ListItemData.ets // 列表项数据模型 └── common/ └── Constants.ets // 常量与样式定义数据流架构用户手势 → SwipeableItem (位移) → 父组件 (删除/添加) → State 更新 → UI 重渲染三、核心技术分析3.1 List 组件与列表渲染HarmonyOS 的List组件是高性能列表容器配合ListItem和ForEach实现数据驱动渲染// model/ListItemData.ets export interface ListItemData { id: number title: string description: string time: string isFavorite: boolean } export function createMockData(): ListItemData[] { const items: ListItemData[] [] const titles [ HarmonyOS 开发笔记, ArkTS 布局技巧, 状态管理指南, 组件化实践, 网络请求封装, 数据持久化方案, 动画与过渡, 性能优化专题, 调试工具使用, 发布上架流程 ] for (let i 0; i titles.length; i) { items.push({ id: i 1, title: titles[i], description: 第 ${i 1} 篇技术文章, time: 2025-01-${String(i 10).padStart(2, 0)}, isFavorite: i % 3 0 }) } return items }ForEach 渲染列表List({ space: 10 }) { ForEach(this.items, (item: ListItemData, index: number) { ListItem() { SwipeableItem({ item: item, onDelete: () this.deleteItem(index), onToggle: () this.toggleFavorite(index) }) } }) } .width(100%) .height(100%) .divider({ strokeWidth: 1, color: #E8E8E8 })List 组件关键属性属性作用本场景用法space列表项间距10vp增加呼吸感divider分割线浅灰色 1px 分割线edgeEffect边缘弹性效果EdgeEffect.SpringscrollBar滚动条自动显示3.2 滑动删除手势实现滑动删除的核心是手势处理。在 ArkTS 中我们可以通过onTouch事件或PanGesture手势实现滑动检测。这里我们采用状态驱动的方式// components/SwipeableItem.ets Component export struct SwipeableItem { Prop item: ListItemData private onDelete?: () void private onToggle?: () void State private offsetX: number 0 State private isSwiped: boolean false private readonly DELETE_BTN_WIDTH: number 80 private readonly SWIPE_THRESHOLD: number 60 private startX: number 0 build() { Stack() { // 底层删除按钮 Row() { Button(删除) .width(this.DELETE_BTN_WIDTH) .height(100%) .backgroundColor(#FF3B30) .fontColor(Color.White) .fontSize(15) .onClick(() { if (this.onDelete) { this.onDelete() } }) } .width(100%) .height(100%) .justifyContent(FlexAlign.End) // 顶层列表项内容可滑动 Row() { // 图标 Circle({ width: 40, height: 40 }) .fill(this.item.isFavorite ? #FFD700 : #E0E0E0) .margin({ left: 16, right: 12 }) // 文本内容 Column({ space: 4 }) { Text(this.item.title) .fontSize(16) .fontWeight(FontWeight.Medium) .lineHeight(22) Text(this.item.description) .fontSize(13) .fontColor(#888888) Text(this.item.time) .fontSize(12) .fontColor(#AAAAAA) } .alignItems(HorizontalAlign.Start) // 收藏按钮 Button({ type: ButtonType.Circle }) { Image(this.item.isFavorite ? $r(app.media.star_filled) : $r(app.media.star_empty)) .width(24) .height(24) } .width(36) .height(36) .backgroundColor(transparent) .margin({ right: 12 }) .onClick(() { if (this.onToggle) { this.onToggle() } }) } .width(100%) .height(72) .backgroundColor(Color.White) .borderRadius(8) .translate({ x: this.offsetX }) .onTouch((event: TouchEvent) { this.handleTouch(event) }) } .width(100%) .height(72) .clip(true) // 超出部分裁切保持圆角 } handleTouch(event: TouchEvent): void { switch (event.type) { case TouchType.Down: this.startX event.touches[0].x break case TouchType.Move: const currentX event.touches[0].x const deltaX currentX - this.startX // 只允许向左滑动负方向 const newOffset Math.min(0, Math.max(-this.DELETE_BTN_WIDTH, this.offsetX deltaX)) this.offsetX newOffset this.startX currentX break case TouchType.Up: case TouchType.Cancel: if (this.offsetX -this.SWIPE_THRESHOLD) { // 滑过阈值吸附到删除按钮完全显示 this.offsetX -this.DELETE_BTN_WIDTH this.isSwiped true } else { // 未达阈值回弹到原始位置 this.offsetX 0 this.isSwiped false } break } } }滑动逻辑详解TouchType.Down记录触摸起始位置TouchType.Move计算手指位移更新offsetX限制范围在[-DELETE_BTN_WIDTH, 0]TouchType.Up判断是否超过阈值SWIPE_THRESHOLD60vp超过吸附到删除按钮完全显示位置-DELETE_BTN_WIDTH未超过弹性回弹到原始位置0clip(true)裁切超出 Stack 区域的内容保持圆角效果3.3 删除与添加功能删除操作// SwipeDelete.ets 中的删除方法 deleteItem(index: number): void { // 动画效果可以先移除 const deletedItem this.items[index] this.items.splice(index, 1) // 更新状态触发 UI 刷新 this.items [...this.items] // 显示提示 this.showToast(已删除「${deletedItem.title}」) }注意事项使用splice删除后需要重新赋值以触发State更新使用扩展运算符[...this.items]创建新数组引用确保 ArkTS 检测到变化删除后显示 Toast 提示用户操作结果添加操作// 添加新项目 addItem(title: string, description: string): void { const newItem: ListItemData { id: Date.now(), // 使用时间戳生成唯一 ID title: title, description: description, time: new Date().toLocaleDateString(zh-CN), isFavorite: false } this.items [newItem, ...this.items] // 插到列表顶部 this.showToast(已添加新项目) }添加弹窗组件// components/AddItemDialog.ets Component export struct AddItemDialog { State private title: string State private description: string private onConfirm?: (title: string, desc: string) void private onCancel?: () void State private visible: boolean false build() { if (this.visible) { Stack() { // 遮罩层 Rect() .width(100%) .height(100%) .fill(#80000000) .onClick(() this.dismiss()) // 对话框 Column({ space: 16 }) { Text(添加新项目) .fontSize(20) .fontWeight(FontWeight.Bold) TextInput({ placeholder: 标题, text: this.title }) .width(100%) .height(44) .backgroundColor(#F5F5F5) .borderRadius(8) .padding({ left: 12 }) .onChange((v: string) { this.title v }) TextInput({ placeholder: 描述, text: this.description }) .width(100%) .height(44) .backgroundColor(#F5F5F5) .borderRadius(8) .padding({ left: 12 }) .onChange((v: string) { this.description v }) Row({ space: 16 }) { Button(取消) .layoutWeight(1) .height(40) .backgroundColor(#E0E0E0) .fontColor(#333333) .borderRadius(8) .onClick(() this.dismiss()) Button(确认) .layoutWeight(1) .height(40) .backgroundColor(#007AFF) .fontColor(Color.White) .borderRadius(8) .onClick(() { if (this.title.trim().length 0 this.onConfirm) { this.onConfirm(this.title.trim(), this.description.trim()) this.dismiss() } }) } .width(100%) } .width(85%) .padding(24) .backgroundColor(Color.White) .borderRadius(16) } .width(100%) .height(100%) } } show(): void { this.title this.description this.visible true } dismiss(): void { this.visible false } }3.4 Toggle 删除按钮切换除了滑动删除我们还实现一个「切换删除模式」的按钮让用户通过点击进入删除状态// 主页面中的 Toggle 按钮 State private isDeleteMode: boolean false build() { Column() { // 顶部栏 Row() { Text(滑动删除列表) .fontSize(22) .fontWeight(FontWeight.Bold) Blank() // 切换删除模式 Toggle({ type: ToggleType.Switch, isOn: this.isDeleteMode }) .onChange((isOn: boolean) { this.isDeleteMode isOn }) Text(this.isDeleteMode ? 删除模式 : 滑动模式) .fontSize(13) .fontColor(this.isDeleteMode ? #FF3B30 : #888888) // 添加按钮 Button({ type: ButtonType.Circle }) { Image($r(app.media.add_icon)) .width(24) .height(24) } .width(36) .height(36) .backgroundColor(#007AFF) .margin({ left: 8 }) .onClick(() { this.dialog.show() }) } .width(92%) .padding({ top: 16, bottom: 8 }) // 列表 List({ space: 10 }) { ForEach(this.items, (item: ListItemData, index: number) { ListItem() { if (this.isDeleteMode) { // 删除模式下显示删除按钮 Row() { Text(item.title) .fontSize(16) .layoutWeight(1) Button(删除) .width(60) .height(32) .backgroundColor(#FF3B30) .fontColor(Color.White) .fontSize(13) .borderRadius(6) .onClick(() this.deleteItem(index)) } .width(100%) .height(56) .padding({ left: 16, right: 8 }) .backgroundColor(Color.White) .borderRadius(8) } else { // 滑动模式下使用可滑动组件 SwipeableItem({ item: item, onDelete: () this.deleteItem(index), onToggle: () this.toggleFavorite(index) }) } } }) } .width(92%) .layoutWeight(1) } .width(100%) .height(100%) .backgroundColor(#F0F0F0) }Toggle 组件说明属性类型说明typeToggleTypeSwitch / Checkbox / ButtonisOnboolean当前开关状态onChangecallback状态变化回调通过Toggle切换两种模式用户可以选择自己习惯的操作方式。四、HarmonyOS 特性深度解析4.1 手势系统ArkTS 的手势系统支持多种手势识别器手势说明适用场景TapGesture点击按钮点击LongPressGesture长按触发编辑模式PanGesture拖拽滑动删除SwipeGesture快速滑动翻页PinchGesture双指缩放图片缩放本例使用onTouch事件实现滑动因为其提供了更细粒度的触摸控制。也可以改用PanGesture手势SwipeableItem() .gesture( PanGesture({ direction: PanDirection.Left }) .onActionStart((event: GestureEvent) { ... }) .onActionUpdate((event: GestureEvent) { ... }) .onActionEnd((event: GestureEvent) { ... }) )4.2 translate 属性与动画translate属性用于移动组件的渲染位置不影响布局占位.translate({ x: this.offsetX })结合animateTo可以实现平滑动画// 回弹动画 animateTo({ duration: 200, curve: Curve.Friction }, () { this.offsetX 0 })曲线类型对比Curve效果使用场景Linear匀速简单移动Friction带阻尼的减速自然回弹Ease渐入渐出平滑动画Spring弹簧效果弹性交互4.3 clip 属性裁切clip属性控制内容超出容器范围时的显示方式Stack() .clip(true) // 裁切超出部分 // 或 .clip(false) // 允许内容溢出在滑动删除中clip(true)保证删除按钮在列表项圆角范围内优雅显示。五、UI/UX 设计要点5.1 交互反馈设计视觉反馈滑动时列表项跟随手指移动提供实时位置反馈触感反馈删除时调用震动 APIvibrator.vibrate(30)Toast 提示操作完成后显示轻量提示回弹动画未达到阈值时弹性回弹模拟真实物理效果5.2 删除确认机制为防止误操作可添加二次确认deleteWithConfirm(index: number): void { AlertDialog.show({ title: 确认删除, message: 确定要删除「${this.items[index].title}」吗, primaryButton: { value: 取消, action: () {} }, secondaryButton: { value: 删除, action: () { this.deleteItem(index) } } }) }5.3 空状态展示当列表为空时显示友好的空状态提示if (this.items.length 0) { Column() { Image($r(app.media.empty_icon)) .width(120) .height(120) Text(暂无数据点击右上角添加) .fontSize(14) .fontColor(#999999) .margin({ top: 16 }) } .width(100%) .height(100%) .justifyContent(FlexAlign.Center) }六、最佳实践总结6.1 性能优化key 属性为ForEach提供唯一 key提升 diff 效率ForEach(this.items, (item: ListItemData) { ListItem() { ... } }, (item: ListItemData) item.id.toString())避免全量重建数组操作后使用浅拷贝触发更新而非重新创建整个数组LazyForEach大量数据时使用LazyForEach进行虚拟列表渲染6.2 错误处理边界情况空列表删除、删除最后一个项目、快速连续删除同步状态确保删除后列表索引与数据状态一致异常恢复删除失败时恢复数据6.3 无障碍访问为删除按钮添加accessibilityText描述确保滑动操作有替代的按钮操作方式使用Toggle提供模式切换兼顾不同用户习惯七、扩展与演进7.1 多选批量删除State selectedIds: Setnumber new Set() toggleSelection(id: number): void { if (this.selectedIds.has(id)) { this.selectedIds.delete(id) } else { this.selectedIds.add(id) } } batchDelete(): void { this.items this.items.filter(item !this.selectedIds.has(item.id)) this.selectedIds.clear() }7.2 拖拽排序配合onDragStart/onDragEnter事件实现拖拽排序ListItem() .onDragStart(() { ... }) .onDrop((event: DragEvent) { ... })7.3 撤销删除使用栈结构保存删除历史private undoStack: ListItemData[] [] deleteItem(index: number): void { const deleted this.items[index] this.undoStack.push(deleted) this.items.splice(index, 1) this.items [...this.items] showToast(已删除可撤销) } undoDelete(): void { if (this.undoStack.length 0) { const restored this.undoStack.pop() this.items [restored!, ...this.items] } }八、结语滑动删除列表是移动应用中最常见也最实用的交互模式之一。通过本项目的学习我们掌握了List ForEach构建数据驱动列表onTouch 事件实现手势滑动检测translate 属性实现内容位移Toggle 组件切换操作模式状态管理在增删操作中的应用这些技能在笔记应用、任务管理、消息列表、购物车等场景中广泛适用是 ArkTS 开发者的必会技能。

相关新闻

# 47号应用:标签页 — 顶部 Tab 切换的内容展示设计

# 47号应用:标签页 — 顶部 Tab 切换的内容展示设计

一、应用概述 标签页(Top Tabs Demo)是一个顶部标签切换应用,四个标签(推荐、关注、热门、附近)对应不同的内容页面,点击标签切换并带有下划线指示器。这是典型的顶部导航模式,广泛用于新闻客户…

2026/7/31 14:44:10阅读更多 →
# HarmonyOS ArkTS 表单验证实战(二):正则表达式与输入校验

# HarmonyOS ArkTS 表单验证实战(二):正则表达式与输入校验

摘要:表单验证是移动应用开发中最常见的需求之一。本文深入讲解如何在 HarmonyOS ArkTS 中构建健壮的表单验证系统,涵盖正则表达式(邮箱、11位手机号)、用户名长度校验、密码一致性验证以及错误信息实时显示等完整技术栈。一、项目…

2026/7/31 14:44:10阅读更多 →
重新定义量化策略验证:基于PyQt与Backtrader的可视化回测平台架构解析

重新定义量化策略验证:基于PyQt与Backtrader的可视化回测平台架构解析

重新定义量化策略验证:基于PyQt与Backtrader的可视化回测平台架构解析 【免费下载链接】backtrader-pyqt-ui 项目地址: https://gitcode.com/gh_mirrors/bac/backtrader-pyqt-ui 在量化交易领域,策略验证的黑盒化与结果分析的低效率一直是困扰技…

2026/7/31 14:44:10阅读更多 →
多Agent系统开发实战:FastAPI+Vue3+LangGraph论文写作平台搭建

多Agent系统开发实战:FastAPI+Vue3+LangGraph论文写作平台搭建

这类多Agent系统最值得先看的不是功能列表,而是能不能在普通开发环境里稳定跑起来,以及从单任务到批量任务的处理流程是否清晰。我一般会先拆解成三个关键部分:后端FastAPI的异步任务管理、前端Vue3的界面交互逻辑,以及LangGraph多…

2026/7/31 16:04:52阅读更多 →
118、YOLOv8改进实战:Copy-Paste增强——基于实例分割掩码的样本复制与粘贴技术

118、YOLOv8改进实战:Copy-Paste增强——基于实例分割掩码的样本复制与粘贴技术

118、YOLOv8改进实战:Copy-Paste增强——基于实例分割掩码的样本复制与粘贴技术 从一次翻车现场说起 去年做工业缺陷检测项目,客户给的数据集只有800张,缺陷类别倒是有12种。训练出来的模型在验证集上mAP有0.78,一上产线就原形毕露——漏检率飙到30%。翻看badcase,发现模…

2026/7/31 16:04:52阅读更多 →
Playwright元素定位实战:从原理到高频场景的健壮写法

Playwright元素定位实战:从原理到高频场景的健壮写法

1. 项目概述与核心价值 最近在几个大型前端项目的回归测试中,我再次深刻体会到UI自动化的重要性。手动点击几百个页面,检查每个交互状态,不仅耗时耗力,还容易因为疲劳而出错。在尝试了Selenium、Cypress等工具后,我最终…

2026/7/31 16:04:52阅读更多 →
H100硬件加速的单流音视频同步方案解析

H100硬件加速的单流音视频同步方案解析

1. 项目概述:极简单流架构的音视频同步方案 这个开源项目提出了一种突破性的音视频同步解决方案,采用单流架构设计,在H100等高性能硬件上可实现2秒快速出片。作为音视频处理领域的SOTA(State-of-the-art)基座&#xff…

2026/7/31 16:04:52阅读更多 →
精选联盟爆品怎么看?选品运营3步避坑清退

精选联盟爆品怎么看?选品运营3步避坑清退

每日清退来了:精选联盟商品为什么不能只看销量,还要看品退率和好评率? 在抖音电商精选联盟的选品逻辑中,仅凭高销量无法确保商品长期存活。根据《关于修订〈精选联盟平台管理规则〉的公示通知》,系统会每日校验商品指标…

2026/7/31 16:04:52阅读更多 →
51单片机驱动16×16 LED点阵显示汉字:从硬件原理到动态扫描编程实战

51单片机驱动16×16 LED点阵显示汉字:从硬件原理到动态扫描编程实战

1. 项目概述与核心价值 最近在整理一些老项目,翻出来一个用51单片机驱动1616 LED点阵显示汉字的仿真工程,感觉挺有意思的。这个项目虽然基础,但麻雀虽小五脏俱全,它几乎涵盖了单片机开发中从硬件原理、驱动芯片控制、字模提取到动…

2026/7/31 16:02:52阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/30 15:03:16阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/30 12:22:27阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/30 15:13:02阅读更多 →
物理复制比逻辑复制好在哪?数据库复制原理详解

物理复制比逻辑复制好在哪?数据库复制原理详解

数据库复制是把主库数据同步到备库的机制,分为逻辑复制和物理复制两种。逻辑复制传输的是 SQL 语句或行变更事件,物理复制传输的是存储引擎底层的物理日志。阿里云 PolarDB(云原生数据库)采用物理复制,在同步延迟、数据…

2026/7/31 0:00:40阅读更多 →
BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh_mirrors/bi/Bilib…

2026/7/31 0:00:41阅读更多 →
有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

当前,游戏行业的“DataAI融合”已从概念验证进入价值落地阶段。根据IDC 2025年数据,中国AI游戏云市场规模已达18.6亿元;同时,游戏研发环节AI渗透率高达86%,生成式AI内容普及率超过50%。面对庞大的市场,游戏…

2026/7/31 0:00:41阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/31 0:49:33阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/31 5:08:18阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/31 16:02:17阅读更多 →