鸿蒙原生 ArkTS 布局实战:List 滑动删除(SwipeToDelete)完全指南


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、引言

在移动应用开发中,滑动操作(Swipe Action)已经成为用户最熟悉的交互手势之一。无论是 iOS 的 Mail 应用中滑动标记已读,还是微信中滑动删除聊天记录,滑动操作为用户提供了一种快捷、直观、高效的交互方式,避免了长按菜单或进入编辑模式的繁琐步骤。

HarmonyOS NEXT 作为华为全场景智能生态的操作系统,在其原生声明式 UI 框架 ArkTS 中,提供了极其优雅的滑动操作 API——.swipeAction()。通过这个 API,开发者可以轻松地为 List 列表项添加左右滑动面板,实现删除、收藏、标记等常见操作。

本篇文章将从一个完整的实战项目出发,带你从零掌握 ArkTS 中 List 滑动删除的完整实现。全文将涵盖:

  • ArkTS 项目结构与页面路由
  • List 组件的基础使用
  • .swipeAction() API 的详细参数解析
  • @Builder@Component 在滑动面板中的正确用法
  • ArkTS 数据模型与状态管理的注意事项
  • 常见编译错误与解决方案
  • 从 API 23 升级到 API 24 的适配要点

二、项目背景与环境配置

2.1 开发环境要求

在开始之前,请确保你的开发环境满足以下条件:

环境项 要求
操作系统 Windows 10/11 或 macOS
开发工具 DevEco Studio 5.0+
SDK 版本 HarmonyOS SDK API 24(7.0.0)
语言版本 ArkTS(基于 TypeScript 5.0+)
目标设备 HarmonyOS NEXT 模拟器或真机

2.2 API 版本升级说明(23 → 24)

本项目的初始模板使用的是 API 23(SDK 6.1.0),但本文所有代码均已适配 API 24(SDK 7.0.0)。升级到 API 24 后,主要变化包括:

  1. 编译规则更严格:对类型检查、空安全等有更细粒度的管控
  2. 废弃 API 标记:部分 API(如 promptAction.showToast)标记为 deprecated,推荐迁移到新的 @ohos.arkui.dialog 模块
  3. 性能优化:List 组件的懒加载和复用机制有底层改进

build-profile.json5 中确认 SDK 版本配置:

{
  "app": {
    "products": [
      {
        "name": "default",
        "targetSdkVersion": "7.0.0(24)",
        "compatibleSdkVersion": "7.0.0(24)",
        "runtimeOS": "HarmonyOS"
      }
    ]
  }
}

2.3 项目结构概览

一个标准的 HarmonyOS NEXT 工程结构如下:

ap06/
├── AppScope/                 # 应用全局配置
│   └── app.json5             # 应用名称、图标等元信息
├── entry/                    # 模块目录
│   ├── src/main/
│   │   ├── ets/
│   │   │   ├── entryability/       # Ability 生命周期
│   │   │   │   └── EntryAbility.ets # 应用入口,负责加载页面
│   │   │   └── pages/
│   │   │       ├── Index.ets       # 默认页面(Hello World)
│   │   │       └── SwipeToDeleteDemo.ets  # 我们的滑动删除页面
│   │   └── resources/              # 资源文件
│   └── build-profile.json5         # 模块构建配置
├── build-profile.json5             # 项目级构建配置
└── oh-package.json5                # 包管理配置

2.4 页面路由注册

在 HarmonyOS NEXT 中,所有页面都需要在 main_pages.json 中注册。我们添加了新页面后,需要编辑 entry/src/main/resources/base/profile/main_pages.json

{
  "src": [
    "pages/Index",
    "pages/SwipeToDeleteDemo"
  ]
}

然后修改 EntryAbility.ets 中的启动页面:

// 将默认的 pages/Index 改为我们的新页面
windowStage.loadContent('pages/SwipeToDeleteDemo', (err) => {
  if (err.code) {
    hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
    return;
  }
  hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
});

三、核心技术:List + .swipeAction()

3.1 List 组件概述

List 是 ArkTS 提供的高性能列表容器,它支持:

  • 懒加载:只渲染可视区域内的列表项,大量数据时依然流畅
  • 复用机制:离屏的 ListItem 会被回收复用,减少内存开销
  • 方向控制:支持垂直(默认)和水平两种滚动方向
  • 粘性标题:支持分组列表的粘性标题(sticky header)
  • 滑动操作:通过 .swipeAction() 为每一项添加滑动面板

基本用法:

List() {
  ForEach(this.dataArray, (item: DataType) => {
    ListItem() {
      // 你的列表项内容
      Text(item.title)
    }
  }, (item: DataType) => item.id.toString())
}
.width('100%')
.height('100%')

3.2 .swipeAction() API 深度解析

.swipeAction()ListItem 组件的方法,用于为列表项添加滑动操作面板。它的完整签名如下:

ListItem()
  .swipeAction(value: SwipeActionOptions)

其中 SwipeActionOptions 接口的定义为:

interface SwipeActionOptions {
  start?: SwipeActionItem;      // 起始侧面板(LTR 布局中为左侧/向右滑)
  end?: SwipeActionItem;        // 结束侧面板(LTR 布局中为右侧/向左滑)
  edgeEffect?: SwipeEdgeEffect; // 滑动到边缘的弹性效果
}

interface SwipeActionItem {
  builder: CustomBuilder;       // 面板的 UI 构建函数(必须为 @Builder)
  onAction?: () => void;        // 面板被触发时的回调
  offset?: number;              // 面板偏移量
}
参数详解
参数 类型 必填 说明
start SwipeActionItem 用户向右滑动时,从左侧露出的操作面板。常用于"删除"、"置顶"等操作
end SwipeActionItem 用户向左滑动时,从右侧露出的操作面板。常用于"收藏"、"更多"等操作
edgeEffect SwipeEdgeEffect 滑动到边缘时的效果:Spring(弹簧回弹)或 None(无效果)
builder CustomBuilder 面板内容的构建器,必须使用 @Builder 装饰的函数
onAction () => void 滑动面板被触发时的回调函数
关于 CustomBuilder 的重要约束

这是 ArkTS 初学者最容易踩的坑。 .swipeAction()builder 属性类型为 CustomBuilder它不接受 @Component 结构体,只能接受用 @Builder 装饰的函数。

错误写法(❌ 编译报错)

.swipeAction({
  start: {
    builder: DeleteButton()  // ❌ DeleteButton 是 @Component,不能赋值给 CustomBuilder
  }
})

正确写法(✅ 正常工作)

@Builder deleteButtonBuilder(item: NotificationItem) {
  Button('删除')
    .onClick(() => { this.deleteItem(item); })
}

// 在 ListItem 中:
.swipeAction({
  start: {
    builder: (): void => this.deleteButtonBuilder(item)
  }
})

这里还有一个细节:builder 需要用箭头函数包裹 @Builder 的调用,即 (): void => this.deleteButtonBuilder(item),而不是直接传递 this.deleteButtonBuilder(item)。这是因为 CustomBuilder 需要延迟执行,在滑动触发时才构建 UI。

3.3 SwipeEdgeEffect 弹性效果

edgeEffect: SwipeEdgeEffect.Spring  // 推荐

SwipeEdgeEffect.Spring 提供了类似 iOS 的弹簧回弹效果:当用户滑动面板超出边界时,面板会像弹簧一样回弹,带来更自然的物理手感。如果设置为 SwipeEdgeEffect.None,则滑动到边界会直接停止,显得生硬。


四、数据模型设计(ArkTS 注意事项)

4.1 类的定义

在 ArkTS 中定义数据模型时,有一个重要的语法限制不能在构造函数的参数中直接声明并赋值字段

// ❌ 错误写法:arkts-no-ctor-prop-decls
class NotificationItem {
  constructor(public id: number, public title: string, public content: string) {
  }
}
// ✅ 正确写法:先声明字段,再在构造函数中赋值
class NotificationItem {
  id: number = 0;          // 必须提供默认值
  title: string = '';
  content: string = '';

  constructor(id: number, title: string, content: string) {
    this.id = id;          // 在构造函数体中进行赋值
    this.title = title;
    this.content = content;
  }
}

4.2 @State 装饰器与响应式数据

@State 是 ArkTS 中最核心的装饰器之一。被 @State 修饰的变量,当其值发生变化时,框架会自动重新渲染与之绑定的 UI。

@State private notifications: NotificationItem[] = [
  new NotificationItem(1, '系统更新提醒', '您的系统已更新至 HarmonyOS NEXT 5.0...'),
  // ...更多数据
];

当你调用 this.notifications.splice(index, 1) 删除一项后,@State 会检测到数组变化,List 会自动移除对应的列表项并更新界面。无需手动刷新,这是声明式 UI 的核心优势。


五、实战代码解析

下面我们逐段解析 SwipeToDeleteDemo.ets 的核心代码。

5.1 页面入口与布局结构

@Entry
@Component
struct SwipeToDeleteDemo {
  @State private notifications: NotificationItem[] = [ /* 6条数据 */ ];

  build() {
    Column() {
      TitleBar({ title: 'List 滑动删除演示' });  // 顶部标题栏
      TipBanner();                                // 操作提示横幅
      List() { /* 列表内容 */ }                    // 核心列表
      BottomActionBar({ onReset: () => this.resetList() })  // 底部重置按钮
    }
  }
}

整个页面采用从上到下的 Column 布局,分为四个区域:

  1. TitleBar:蓝色的标题栏,显示页面名称
  2. TipBanner:橙色的操作提示条,告诉用户滑动手势的方向
  3. List:占据剩余空间的核心列表区域
  4. BottomActionBar:底部的重置按钮

5.2 列表与滑动操作的绑定

这是整个应用的核心代码:

List() {
  ForEach(this.notifications, (item: NotificationItem) => {
    ListItem() {
      NotificationCard({ item: item })
    }
    .height(80)
    .swipeAction({                           // ← 核心:绑定滑动操作
      start: {                               // 向右滑动 → 左侧露出删除
        builder: (): void => this.deleteButtonBuilder(item)
      },
      end: {                                 // 向左滑动 → 右侧露出收藏
        builder: (): void => this.favoriteButtonBuilder(item)
      },
      edgeEffect: SwipeEdgeEffect.Spring     // 弹性回弹效果
    })
  }, (item: NotificationItem) => item.id.toString())  // key 生成器
}

关键细节:

  • 每个 ListItem 独立绑定 .swipeAction(),互不干扰
  • ForEach 的第三个参数是 key 生成器,使用 item.id.toString() 确保列表项的唯一标识。当列表数据变化时,框架通过 key 来识别哪些项需要增、删、移,而不是全部重新渲染。这是高性能列表的关键
  • height(80) 统一设置每个列表项的高度为 80vp

5.3 @Builder 构建滑动面板

@Builder
deleteButtonBuilder(item: NotificationItem) {
  Button('删除')
    .width(80)
    .height('100%')
    .backgroundColor('#FF3B30')    // iOS 风格的红色
    .fontColor(Color.White)
    .fontSize(16)
    .fontWeight(FontWeight.Medium)
    .borderRadius(0)               // 直角,与列表项融为一体
    .onClick(() => {
      this.deleteItem(item);       // 点击执行删除
    });
}

@Builder
favoriteButtonBuilder(item: NotificationItem) {
  Button('收藏')
    .width(80)
    .height('100%')
    .backgroundColor('#007AFF')    // 蓝色
    .fontColor(Color.White)
    .fontSize(16)
    .borderRadius(0)
    .onClick(() => {
      this.favoriteItem(item);     // 点击执行收藏
    });
}

设计要点:

  • 按钮的 .width(80) 决定了滑动面板的宽度。你可以根据实际需求调整
  • .height('100%') 让按钮撑满整个 ListItem 的高度
  • .borderRadius(0) 使用直角,让按钮与列表项边界完全贴合,视觉上更干净
  • @Builder 函数可以接受参数(item: NotificationItem),将当前列表项的数据传入

5.4 删除与状态更新

private deleteItem(item: NotificationItem): void {
  let index = this.notifications.findIndex(n => n.id === item.id);
  if (index !== -1) {
    this.notifications.splice(index, 1);  // 从响应式数组中移除
    
    promptAction.showToast({
      message: `已删除:「${item.title}」`,
      duration: 1500
    });
  }
}

删除的流程非常简洁:

  1. 通过 findIndex 找到要删除项在数组中的索引
  2. 调用 splice(index, 1) 从数组中移除一项
  3. @State 检测到数组变化,自动触发 UI 更新
  4. showToast 给用户一个操作反馈提示

5.5 重置列表

private resetList(): void {
  this.notifications = [
    new NotificationItem(1, '系统更新提醒', '您的系统已更新至 HarmonyOS NEXT 5.0...'),
    // ... 重新初始化为全部 6 条数据
  ];
}

resetList()notifications 数组重新赋值为初始数据。注意:这里直接赋值一个新数组,而不是修改原数组。@State 装饰器会检测到引用变化(数组地址改变),从而触发全量刷新。如果只想修改数组内容而不改变引用,则需要用 splicepush 等变异方法。


六、子组件设计与复用

将一个大型页面拆分为多个小的 @Component,是 ArkTS 推荐的最佳实践。本示例将页面拆分为 4 个子组件:

6.1 TitleBar(标题栏)

@Component
struct TitleBar {
  private title: string = '';

  build() {
    Row() {
      Text(this.title)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.White)
    }
    .width('100%')
    .height(56)
    .backgroundColor('#0A59F7')  // 鸿蒙品牌蓝色
    .padding({ left: 20, right: 20 })
  }
}

6.2 TipBanner(操作提示条)

@Component
struct TipBanner {
  build() {
    Row() {
      Text('👉 向右滑动列表项 → 删除  |  👈 向左滑动 → 收藏')
        .fontSize(13)
        .fontColor('#666666')
        .textAlign(TextAlign.Center)
    }
    .width('100%')
    .padding({ top: 12, bottom: 12 })
    .backgroundColor('#FFEEDD')
  }
}

这个提示条虽然简单,但对用户体验至关重要——用户需要被引导才知道列表项是可以滑动的。这也是移动端设计中的一个重要原则:可见性(Discoverability)。

6.3 NotificationCard(通知卡片)

@Component
struct NotificationCard {
  private item: NotificationItem = new NotificationItem(0, '', '');

  build() {
    Row() {
      // 左侧圆形图标(首字头像)
      Circle()
        .width(40).height(40)
        .fill('#0A59F7').opacity(0.85)
        .margin({ right: 14 })
        .overlay(this.iconBuilder(this.item.title.charAt(0)))

      // 右侧文字区域
      Column() {
        Text(this.item.title)
          .fontSize(16).fontWeight(FontWeight.Medium)
          .maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
        Text(this.item.content)
          .fontSize(13).fontColor('#999999')
          .maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
          .margin({ top: 4 })
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
    }
    .padding({ left: 16, right: 16 })
    .backgroundColor(Color.White)
    .alignItems(VerticalAlign.Center)
  }

  @Builder
  iconBuilder(char: string) {
    Text(char)
      .fontColor(Color.White).fontSize(18).fontWeight(FontWeight.Bold)
  }
}

注意Circle().overlay() 方法同样接收 CustomBuilder 类型,因此也需要用 @Builder 函数构建内部的 Text。

6.4 BottomActionBar(底部操作栏)

@Component
struct BottomActionBar {
  private onReset?: () => void;

  build() {
    Row() {
      Button('🔄 重置列表')
        .width(160).height(40)
        .backgroundColor('#0A59F7')
        .fontColor(Color.White).fontSize(15)
        .borderRadius(20)
        .onClick(() => { this.onReset?.(); })
    }
    .width('100%')
    .height(64)
    .justifyContent(FlexAlign.Center)
    .backgroundColor(Color.White)
    .shadow({ radius: 4, color: '#20000000' })
  }
}

子组件通信方式BottomActionBar 通过 private onReset?: () => void 定义回调函数属性,父组件在创建时传入 onReset: () => this.resetList()。这是 ArkTS 中父子组件通信的标准模式。


七、完整代码清单

以下是 SwipeToDeleteDemo.ets 的完整代码(约 307 行):

/*
 * 滑动删除(SwipeToDelete)示例页面
 * ================================
 * 布局要点:
 * 1. List + .swipeAction() 实现列表项滑动操作
 * 2. start 参数:向右滑动时在左侧展示的操作面板(删除按钮)
 * 3. end   参数:向左滑动时在右侧展示的操作面板(收藏按钮)
 * 4. 通过 @State 驱动数据变化,删除后自动刷新 UI
 * 5. 使用 @Builder 自定义构建滑动操作面板(ArkTS 要求 CustomBuilder 类型)
 */

import { promptAction } from '@kit.ArkUI';

// ----- 数据模型 -----
class NotificationItem {
  id: number = 0;
  title: string = '';
  content: string = '';

  constructor(id: number, title: string, content: string) {
    this.id = id;
    this.title = title;
    this.content = content;
  }
}

// ----- 主页面 -----
@Entry
@Component
struct SwipeToDeleteDemo {
  @State private notifications: NotificationItem[] = [
    new NotificationItem(1, '系统更新提醒', '您的系统已更新至 HarmonyOS NEXT 5.0,点击查看详情。'),
    new NotificationItem(2, '新消息通知', '张三 回复了您的评论:"感谢分享!"'),
    new NotificationItem(3, '应用下载完成', '《备忘录》应用已下载完成,点击安装。'),
    new NotificationItem(4, '安全提醒', '检测到异地登录,建议立即修改密码。'),
    new NotificationItem(5, '日历提醒', '您有一个会议将于 15:00 开始,请准时参加。'),
    new NotificationItem(6, '订阅更新', '您关注的专栏「鸿蒙开发实战」发布了新文章。'),
  ];

  build() {
    Column() {
      TitleBar({ title: 'List 滑动删除演示' })
      TipBanner()
      List() {
        ForEach(this.notifications, (item: NotificationItem) => {
          ListItem() {
            NotificationCard({ item: item })
          }
          .height(80)
          .swipeAction({
            start: {
              builder: (): void => this.deleteButtonBuilder(item)
            },
            end: {
              builder: (): void => this.favoriteButtonBuilder(item)
            },
            edgeEffect: SwipeEdgeEffect.Spring
          })
        }, (item: NotificationItem) => item.id.toString())
      }
      .width('100%')
      .layoutWeight(1)
      .backgroundColor(Color.Transparent)
      BottomActionBar({ onReset: () => this.resetList() })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }

  @Builder
  deleteButtonBuilder(item: NotificationItem) {
    Button('删除')
      .width(80).height('100%')
      .backgroundColor('#FF3B30')
      .fontColor(Color.White).fontSize(16)
      .fontWeight(FontWeight.Medium).borderRadius(0)
      .onClick(() => { this.deleteItem(item); });
  }

  @Builder
  favoriteButtonBuilder(item: NotificationItem) {
    Button('收藏')
      .width(80).height('100%')
      .backgroundColor('#007AFF')
      .fontColor(Color.White).fontSize(16).borderRadius(0)
      .onClick(() => { this.favoriteItem(item); });
  }

  private deleteItem(item: NotificationItem): void {
    let index = this.notifications.findIndex(n => n.id === item.id);
    if (index !== -1) {
      this.notifications.splice(index, 1);
      promptAction.showToast({
        message: `已删除:「${item.title}」`,
        duration: 1500
      });
    }
  }

  private favoriteItem(item: NotificationItem): void {
    promptAction.showToast({
      message: `已收藏:「${item.title}」`,
      duration: 1500
    });
  }

  private resetList(): void {
    this.notifications = [
      new NotificationItem(1, '系统更新提醒', '您的系统已更新至 HarmonyOS NEXT 5.0,点击查看详情。'),
      new NotificationItem(2, '新消息通知', '张三 回复了您的评论:"感谢分享!"'),
      new NotificationItem(3, '应用下载完成', '《备忘录》应用已下载完成,点击安装。'),
      new NotificationItem(4, '安全提醒', '检测到异地登录,建议立即修改密码。'),
      new NotificationItem(5, '日历提醒', '您有一个会议将于 15:00 开始,请准时参加。'),
      new NotificationItem(6, '订阅更新', '您关注的专栏「鸿蒙开发实战」发布了新文章。'),
    ];
    promptAction.showToast({ message: '列表已重置', duration: 1000 });
  }
}

@Component
struct TitleBar {
  private title: string = '';
  build() {
    Row() {
      Text(this.title).fontSize(20).fontWeight(FontWeight.Bold).fontColor(Color.White)
    }
    .width('100%').height(56).backgroundColor('#0A59F7').padding({ left: 20, right: 20 })
  }
}

@Component
struct TipBanner {
  build() {
    Row() {
      Text('👉 向右滑动列表项 → 删除  |  👈 向左滑动 → 收藏')
        .fontSize(13).fontColor('#666666').textAlign(TextAlign.Center)
    }
    .width('100%').padding({ top: 12, bottom: 12 }).backgroundColor('#FFEEDD')
  }
}

@Component
struct BottomActionBar {
  private onReset?: () => void;
  build() {
    Row() {
      Button('🔄 重置列表').width(160).height(40)
        .backgroundColor('#0A59F7').fontColor(Color.White).fontSize(15).borderRadius(20)
        .onClick(() => { this.onReset?.(); })
    }
    .width('100%').height(64).justifyContent(FlexAlign.Center)
    .backgroundColor(Color.White).shadow({ radius: 4, color: '#20000000' })
  }
}

@Component
struct NotificationCard {
  private item: NotificationItem = new NotificationItem(0, '', '');

  build() {
    Row() {
      Circle().width(40).height(40).fill('#0A59F7').opacity(0.85)
        .margin({ right: 14 })
        .overlay(this.iconBuilder(this.item.title.charAt(0)))
      Column() {
        Text(this.item.title).fontSize(16).fontWeight(FontWeight.Medium)
          .maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis }).width('100%')
        Text(this.item.content).fontSize(13).fontColor('#999999')
          .maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis }).width('100%')
          .margin({ top: 4 })
      }
      .alignItems(HorizontalAlign.Start).layoutWeight(1)
    }
    .width('100%').height('100%')
    .padding({ left: 16, right: 16 }).backgroundColor(Color.White)
    .alignItems(VerticalAlign.Center)
  }

  @Builder
  iconBuilder(char: string) {
    Text(char).fontColor(Color.White).fontSize(18).fontWeight(FontWeight.Bold)
  }
}

八、常见编译错误与解决方案

在实际开发过程中,ArkTS 编译器会比标准 TypeScript 更加严格。以下是我们踩过的坑和解决方案:

8.1 ERROR: arkts-no-ctor-prop-decls

ERROR: Declaring fields in "constructor" is not supported (arkts-no-ctor-prop-decls)

原因:ArkTS 不支持 TypeScript 中的构造函数参数属性简写(即 constructor(public id: number) 这种写法)。

解决方案:在类体中先声明所有字段并提供默认值,然后在构造函数体中逐个赋值。

// ✅ 正确
class Item {
  id: number = 0;
  constructor(id: number) {
    this.id = id;
  }
}

// ❌ 错误
class Item {
  constructor(public id: number) {}
}

8.2 ERROR: CustomBuilder 类型不匹配

ERROR: Type 'DeleteButton' is not assignable to type 'CustomBuilder'

原因.swipeAction()builder 属性要求 CustomBuilder 类型,但直接传入了一个 @Component 结构体的实例。

解决方案:将滑动面板的定义从 @Component 改为 @Builder 函数。

// ✅ 正确
@Builder
deleteButtonBuilder(item: Item) {
  Button('删除').onClick(() => { this.deleteItem(item); })
}

// ❌ 错误
.swipeAction({
  start: { builder: DeleteButton() }
})

8.3 ERROR: overlay 参数类型不匹配

ERROR: Argument of type 'TextAttribute' is not assignable to parameter of type 'string | CustomBuilder'

原因Circle().overlay() 方法接受 CustomBuilder 类型,但直接链式调用 .fontColor() 等 Text 属性方法返回的是 TextAttribute,与 CustomBuilder 不兼容。

解决方案:同样使用 @Builder 函数构建 overlay 内容。

// ✅ 正确
Circle()
  .overlay(this.iconBuilder(char))

@Builder
iconBuilder(char: string) {
  Text(char).fontColor(Color.White).fontSize(18)
}

// ❌ 错误
Circle()
  .overlay(Text(char).fontColor(Color.White))

8.4 警告: showToast 已废弃

WARN: 'showToast' has been deprecated.

原因:在 API 24 中,promptAction.showToast() 已被标记为 deprecated,推荐迁移到新的 Toast API。

解决方案:对于 API 24 及以上版本,推荐使用 @ohos.arkui.dialog 模块中的 showToast 方法:

// API 24 推荐方式
import { showToast } from '@ohos.arkui.dialog';

// 使用
showToast({ message: '已删除', duration: 1500 });

不过,promptAction.showToast() 在 API 24 中仍然可以正常工作,只是会有编译警告。如果你暂时不想迁移,可以在 build-profile.json5strictMode 中配置忽略 deprecated 检查,或者简单地继续使用旧 API——不会影响功能。


九、扩展与最佳实践

9.1 多按钮滑动面板

当需要在滑动面板中放置多个按钮时,可以使用 Row 布局:

@Builder
multiActionBuilder(item: NotificationItem) {
  Row() {
    Button('置顶').width(60).height('100%').backgroundColor('#FF9500')
      .onClick(() => { this.pinItem(item); })
    Button('标记').width(60).height('100%').backgroundColor('#007AFF')
      .onClick(() => { this.markItem(item); })
    Button('删除').width(60).height('100%').backgroundColor('#FF3B30')
      .onClick(() => { this.deleteItem(item); })
  }
}

9.2 确认删除对话框

为了防止误删除,可以在点击删除按钮时弹出确认对话框:

import { AlertDialog } from '@kit.ArkUI';

private confirmDelete(item: NotificationItem): void {
  AlertDialog.show({
    title: '确认删除',
    message: `确定要删除「${item.title}」吗?`,
    primaryButton: {
      value: '取消',
      action: () => {}
    },
    secondaryButton: {
      value: '删除',
      fontColor: '#FF3B30',
      action: () => { this.deleteItem(item); }
    }
  });
}

9.3 性能优化:数据量大时的考量

当列表数据超过 100 条时,建议:

  1. 使用 LazyForEach 替代 ForEach,实现真正的虚拟列表
  2. 避免复杂的 @Builder 中的嵌套组件,保持滑动面板的轻量化
  3. 合理设置 ListItem 的高度,尽量固定高度以优化复用效率
// 大数据量时推荐使用 LazyForEach
List() {
  LazyForEach(this.dataSource, (item: Item) => {
    ListItem() {
      ItemView({ item: item })
    }
    .swipeAction({ /* ... */ })
  }, (item: Item) => item.key)
}

9.4 无障碍访问(Accessibility)

为滑动按钮添加无障碍标签,方便视障用户使用:

Button('删除')
  .accessibilityText('删除此通知')
  .accessibilityDescription('点击后将从此列表中移除该条通知')

十、总结

本文通过一个完整的实战项目,详细讲解了在 HarmonyOS NEXT 中使用 ArkTS 实现 List 滑动删除(SwipeToDelete) 的全部技术细节。

核心要点回顾

知识点 核心内容
List 组件 高性能列表容器,支持懒加载和复用
.swipeAction() ListItem 的滑动操作 API,支持 start/end 双方向
@Builder 与 @Component 滑动面板必须使用 @Builder(CustomBuilder),不能用 @Component
@State 响应式数据 修改 @State 数组自动触发 UI 刷新
ForEach key 生成器 为每个列表项提供唯一 key,优化 Diff 效率
ArkTS 语法约束 构造函数不能参数声明字段;overlay 需要 @Builder

适用场景

  • 邮件列表中的滑动标记已读/删除
  • 聊天列表的滑动删除/置顶
  • 待办事项应用的滑动完成/删除
  • 文件管理器的滑动更多操作
  • 任何需要快捷操作的列表界面

项目源码

本项目的完整代码位于 entry/src/main/ets/pages/SwipeToDeleteDemo.ets,已通过 API 24 编译验证。你可以直接将其导入到 DevEco Studio 中运行,体验滑动删除的流畅交互。


附录:参考资源


本文由 AtomCode (deepseek-v4-flash) 基于实际项目编译验证生成。博客文件路径:Blog_SwipeToDelete_HarmonyOS_NEXT.md

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐