145.[HarmonyOS NEXT 实战案例七 :List系列] 可选择列表基础篇

2025-06-30 22:19:49
103次阅读
0个评论

[HarmonyOS NEXT 实战案例七 :List系列] 可选择列表基础篇

项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star

效果演示

image.png

image.png

在移动应用开发中,可选择列表是一种常见且实用的交互模式,它允许用户从列表中选择一个或多个项目进行批量操作。本教程将详细讲解如何在HarmonyOS NEXT中实现一个功能完善的可选择列表,以文件管理应用为例,展示如何创建、管理和操作可选择列表。

一、可选择列表概述

可选择列表是List组件的一种特殊应用场景,它具有以下特点:

  1. 多选功能:允许用户同时选择多个列表项
  2. 选择模式切换:可以在普通浏览模式和选择模式之间切换
  3. 批量操作:对选中的项目执行批量操作,如删除、移动、分享等
  4. 全选/取消全选:提供快捷方式选择或取消选择所有项目
  5. 视觉反馈:通过UI变化清晰地指示当前选中的项目

二、数据模型设计

首先,我们需要定义文件数据的接口和示例数据:

interface FileType {
    id: number,
    name: string,
    type: string,
    size: string,
    date: string,
    icon: Resource
}

这个接口定义了文件的基本属性:

  • id:文件的唯一标识符
  • name:文件名称
  • type:文件类型(如docx、xlsx、pdf等)
  • size:文件大小
  • date:修改日期
  • icon:文件图标资源

然后,我们创建示例文件数据:

private files: FileType[] = [
    { id: 1, name: '项目计划.docx', type: 'docx', size: '2.5MB', date: '2023-10-15', icon: $r('app.media.big26') },
    { id: 2, name: '财务报表.xlsx', type: 'xlsx', size: '1.8MB', date: '2023-10-14', icon: $r('app.media.big25') },
    // 更多文件...
]

三、状态管理

为了管理可选择列表的状态,我们需要定义以下状态变量:

// 选中的文件ID列表
@State selectedIds: number[] = []
// 是否处于选择模式
@State isSelectMode: boolean = false
  • selectedIds:存储当前选中的文件ID列表
  • isSelectMode:标记当前是否处于选择模式

四、核心功能实现

1. 切换选择模式

toggleSelectMode() {
    this.isSelectMode = !this.isSelectMode
    if (!this.isSelectMode) {
        this.selectedIds = []
    }
}

这个方法用于切换选择模式的开启和关闭。当退出选择模式时,会清空已选择的项目。

2. 切换选择状态

toggleSelection(id: number) {
    const index = this.selectedIds.indexOf(id)
    if (index === -1) {
        this.selectedIds.push(id)
    } else {
        this.selectedIds.splice(index, 1)
    }
}

这个方法用于切换单个文件的选择状态:如果文件未被选中,则添加到选中列表;如果已被选中,则从列表中移除。

3. 全选和取消全选

// 全选
selectAll() {
    this.selectedIds = this.files.map(file => file.id)
}

// 取消全选
deselectAll() {
    this.selectedIds = []
}

这两个方法分别用于全选和取消全选操作。

4. 获取文件图标

getFileIcon(type: string): Resource {
    switch (type) {
        case 'docx':
            return $r('app.media.big26')
        case 'xlsx':
            return $r('app.media.big25')
        // 其他文件类型...
        default:
            return $r('app.media.big20')
    }
}

这个方法根据文件类型返回对应的图标资源。

五、UI构建

1. 整体布局

build() {
    Column() {
        // 标题栏
        Row() { /* ... */ }
        
        // 文件列表
        List() { /* ... */ }
        
        // 底部操作栏(选择模式下显示)
        if (this.isSelectMode && this.selectedIds.length > 0) {
            Row() { /* ... */ }
        }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#FFFFFF')
}

整体布局采用Column容器,包含三个主要部分:标题栏、文件列表和底部操作栏(仅在选择模式下且有选中项时显示)。

2. 标题栏实现

标题栏会根据当前是否处于选择模式显示不同的内容:

Row() {
    if (this.isSelectMode) {
        // 选择模式下的标题栏
        Image($r('app.media.big2'))
            .width(24)
            .height(24)
            .onClick(() => this.toggleSelectMode())

        Text(`已选择 ${this.selectedIds.length} 项`)
            .fontSize(18)
            .fontWeight(FontWeight.Medium)
            .margin({ left: 16 })

        Blank()

        Button(this.selectedIds.length === this.files.length ? '取消全选' : '全选')
            .fontSize(14)
            .fontColor('#007DFF')
            .backgroundColor('transparent')
            .onClick(() => {
                if (this.selectedIds.length === this.files.length) {
                    this.deselectAll()
                } else {
                    this.selectAll()
                }
            })
    } else {
        // 普通模式下的标题栏
        Text('文件')
            .fontSize(24)
            .fontWeight(FontWeight.Bold)

        Blank()

        Image($r('app.media.01'))
            .width(24)
            .height(24)
            .margin({ right: 16 })

        Image($r('app.media.02'))
            .width(24)
            .height(24)
            .margin({ right: 16 })
            .onClick(() => this.toggleSelectMode())
    }
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor('#F1F3F5')
  • 在选择模式下:显示返回按钮、已选择项数量和全选/取消全选按钮
  • 在普通模式下:显示标题和操作按钮,包括进入选择模式的按钮

3. 文件列表实现

List() {
    ForEach(this.files, (file: FileType) => {
        ListItem() {
            Row() {
                if (this.isSelectMode) {
                    // 选择框
                    Checkbox({ name: file.id.toString() })
                        .select(this.selectedIds.includes(file.id))
                        .margin({ right: 16 })
                        .onChange((value) => {
                            this.toggleSelection(file.id)
                        })
                }

                // 文件图标
                Image(this.getFileIcon(file.type))
                    .width(40)
                    .height(40)
                    .margin({ right: 16 })

                // 文件信息
                Column() {
                    Text(file.name)
                        .fontSize(16)
                        .fontWeight(FontWeight.Medium)

                    Row() {
                        Text(file.size)
                            .fontSize(14)
                            .fontColor('#666666')

                        Text(file.date)
                            .fontSize(14)
                            .fontColor('#666666')
                            .margin({ left: 16 })
                    }
                    .margin({ top: 4 })
                }
                .alignItems(HorizontalAlign.Start)
                .layoutWeight(1)

                if (!this.isSelectMode) {
                    // 更多操作按钮
                    Image($r('app.media.game_icon'))
                        .width(24)
                        .height(24)
                }
            }
            .width('100%')
            .padding({ left: 16, right: 16, top: 12, bottom: 12 })
            .onClick(() => {
                if (this.isSelectMode) {
                    this.toggleSelection(file.id)
                }
            })
        }
        .height(72)
        .backgroundColor(this.selectedIds.includes(file.id) ? '#F0F7FF' : '#FFFFFF')
    })
}
.width('100%')
.layoutWeight(1)
.divider({ // 设置分割线
    strokeWidth: 1,
    color: '#E5E5E5',
    startMargin: this.isSelectMode ? 72 : 72,
    endMargin: 16
})
.multiSelectable(this.isSelectMode)  // 启用选择模式

文件列表的关键点:

  1. 使用ForEach遍历文件数据,为每个文件创建一个ListItem
  2. 在选择模式下显示Checkbox,并绑定选择状态和变更事件
  3. 根据文件是否被选中,设置不同的背景色以提供视觉反馈
  4. 使用multiSelectable属性启用List的多选功能
  5. 设置分割线样式,并根据是否处于选择模式调整起始边距

4. 底部操作栏实现

if (this.isSelectMode && this.selectedIds.length > 0) {
    Row() {
        Button('删除')
            .width(100)
            .height(40)
            .backgroundColor('#F44336')
            .margin({ right: 16 })

        Button('移动')
            .width(100)
            .height(40)
            .backgroundColor('#007DFF')
            .margin({ right: 16 })

        Button('分享')
            .width(100)
            .height(40)
            .backgroundColor('#4CAF50')
    }
    .width('100%')
    .height(72)
    .padding(16)
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#FFFFFF')
    .borderColor('#E5E5E5')
    .borderWidth({ top: 1 })
}

底部操作栏仅在选择模式下且有选中项时显示,包含删除、移动和分享三个操作按钮。

六、关键技术点分析

1. 多选功能的实现

多选功能主要通过以下几个方面实现:

  • 使用@State selectedIds: number[]存储选中项的ID
  • 通过toggleSelection方法管理选中状态
  • 使用List组件的multiSelectable属性启用多选功能
  • 通过Checkbox组件提供选择界面

2. 模式切换的实现

模式切换主要通过@State isSelectMode: boolean状态变量和toggleSelectMode方法实现。当模式切换时,UI会相应地变化:

  • 标题栏内容变化
  • 列表项中显示/隐藏选择框
  • 底部操作栏的显示/隐藏

3. 视觉反馈的实现

为了提供良好的用户体验,代码中实现了多种视觉反馈:

  • 选中项的背景色变化:.backgroundColor(this.selectedIds.includes(file.id) ? '#F0F7FF' : '#FFFFFF')
  • 已选择项数量的显示:Text(已选择 ${this.selectedIds.length} 项)
  • 全选/取消全选按钮文本的动态变化:Button(this.selectedIds.length === this.files.length ? '取消全选' : '全选')

七、代码结构与样式设置

组件部分 主要功能 样式设置
Column 整体容器 宽高100%,白色背景
标题栏Row 显示标题和操作按钮 高56px,左右内边距16px,浅灰背景
List 显示文件列表 宽100%,弹性布局权重1,带分割线
ListItem 单个文件项 高72px,选中时背景色变化
底部Row 批量操作按钮 高72px,内边距16px,居中对齐,顶部边框

八、总结

本教程详细讲解了如何在HarmonyOS NEXT中实现一个功能完善的可选择列表 ,通过本教程,你应该能够掌握HarmonyOS NEXT中可选择列表的基本实现方法,并能够应用到自己的项目中。在进阶篇中,我们将探讨如何增强可选择列表的功能,如拖拽排序、搜索过滤、分类显示等高级特性。

收藏00

登录 后评论。没有帐号? 注册 一个。

全栈若城

  • 0回答
  • 4粉丝
  • 0关注