146.[HarmonyOS NEXT 实战案例七 :List系列] 可选择列表进阶篇 原创

全栈若城
发布于 2025-6-30 15:45
浏览
0收藏

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

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

效果演示

146.[HarmonyOS NEXT 实战案例七 :List系列] 可选择列表进阶篇-鸿蒙开发者社区
146.[HarmonyOS NEXT 实战案例七 :List系列] 可选择列表进阶篇-鸿蒙开发者社区
在基础篇中,我们学习了如何实现一个基本的可选择列表。本篇教程将深入探讨可选择列表的进阶功能和优化技巧,帮助你构建更加强大、用户体验更佳的可选择列表应用。

一、进阶功能概述

在本教程中,我们将探讨以下进阶功能:

  1. 长按触发选择模式:通过长按列表项进入选择模式
  2. 批量操作功能实现:删除、移动、分享等批量操作的具体实现
  3. 选择状态的动画效果:为选择状态变化添加流畅的动画效果
  4. 自定义选择样式:定制选择框和选中状态的视觉效果
  5. 列表项交互优化:优化列表项的点击、长按等交互体验
  6. 状态持久化:保存和恢复选择状态

二、长按触发选择模式

除了通过按钮进入选择模式外,我们还可以实现长按列表项进入选择模式的功能,这是移动应用中常见的交互模式。

// 在ListItem中添加长按手势
.gesture(
    LongPressGesture()
        .onAction(() => {
            if (!this.isSelectMode) {
                this.isSelectMode = true
                this.toggleSelection(file.id)
            }
        })
)

这段代码为ListItem添加了长按手势,当用户长按列表项时,如果当前不是选择模式,则进入选择模式并选中当前项。

三、批量操作功能实现

1. 批量删除功能

// 删除选中的文件
deleteSelectedFiles() {
    // 创建一个对话框确认删除操作
    AlertDialog.show({
        title: '确认删除',
        message: `确定要删除选中的 ${this.selectedIds.length} 个文件吗?`,
        primaryButton: {
            value: '取消',
            action: () => {
                console.info('取消删除')
            }
        },
        secondaryButton: {
            value: '删除',
            action: () => {
                // 执行删除操作
                this.files = this.files.filter(file => !this.selectedIds.includes(file.id))
                this.selectedIds = []
                // 如果删除后没有文件了,退出选择模式
                if (this.files.length === 0) {
                    this.isSelectMode = false
                }
            }
        }
    })
}

这个方法实现了批量删除功能:

  1. 弹出确认对话框
  2. 用户确认后,过滤掉选中的文件
  3. 清空选中列表
  4. 如果删除后没有文件了,退出选择模式

2. 批量移动功能

// 移动选中的文件
moveSelectedFiles() {
    // 这里可以弹出文件夹选择器,让用户选择目标文件夹
    // 示例中仅展示移动逻辑
    const selectedFiles = this.files.filter(file => this.selectedIds.includes(file.id))
    
    // 模拟移动操作,实际应用中应该调用文件系统API
    console.info(`移动 ${selectedFiles.length} 个文件到目标文件夹`)
    
    // 移动成功后,可以从当前列表中移除这些文件
    this.files = this.files.filter(file => !this.selectedIds.includes(file.id))
    this.selectedIds = []
    
    // 显示移动成功提示
    this.showToast(`已移动 ${selectedFiles.length} 个文件`)
}

3. 批量分享功能

// 分享选中的文件
shareSelectedFiles() {
    const selectedFiles = this.files.filter(file => this.selectedIds.includes(file.id))
    
    // 模拟分享操作,实际应用中应该调用系统分享API
    console.info(`分享 ${selectedFiles.length} 个文件`)
    
    // 显示分享成功提示
    this.showToast(`已分享 ${selectedFiles.length} 个文件`)
}

// 显示提示信息
showToast(message: string) {
    // 实现一个简单的Toast提示
    this.toastMessage = message
    this.showToastFlag = true
    
    // 2秒后自动隐藏
    setTimeout(() => {
        this.showToastFlag = false
    }, 2000)
}

四、选择状态的动画效果

为了提升用户体验,我们可以为选择状态的变化添加动画效果。

1. 选中状态的过渡动画

// 在ListItem中添加动画
.animation({
    duration: 300,
    curve: Curve.EaseInOut,
    iterations: 1,
    playMode: PlayMode.Normal
})

这段代码为ListItem添加了动画效果,当背景色变化时(选中/取消选中),会有平滑的过渡效果。

2. 选择模式切换的动画

// 在标题栏中添加动画
.transition({
    type: TransitionType.All,
    opacity: this.isSelectMode ? 1 : 0,
    scale: { x: this.isSelectMode ? 1 : 0.8, y: this.isSelectMode ? 1 : 0.8 },
    translate: { x: this.isSelectMode ? 0 : -50, y: 0 }
})

这段代码为标题栏中的元素添加了过渡效果,当切换选择模式时,元素会有淡入淡出、缩放和平移的动画效果。

五、自定义选择样式

1. 自定义选择框样式

Checkbox({ name: file.id.toString() })
    .select(this.selectedIds.includes(file.id))
    .margin({ right: 16 })
    .onChange((value) => {
        this.toggleSelection(file.id)
    })
    .selectedColor('#007DFF')  // 选中时的颜色
    .width(20)  // 自定义宽度
    .height(20)  // 自定义高度
    .borderRadius(10)  // 圆角边框
    .borderWidth(2)  // 边框宽度
    .borderColor('#CCCCCC')  // 边框颜色

这段代码自定义了Checkbox的样式,包括选中颜色、大小、边框等属性。

2. 自定义选中状态样式

// 自定义选中状态的背景样式
.backgroundColor(this.selectedIds.includes(file.id) ? '#F0F7FF' : '#FFFFFF')
.borderRadius(8)  // 添加圆角
.borderWidth(this.selectedIds.includes(file.id) ? 1 : 0)  // 选中时添加边框
.borderColor('#007DFF')  // 边框颜色
.shadow(this.selectedIds.includes(file.id) ? {
    radius: 6,
    color: 'rgba(0, 125, 255, 0.1)',
    offsetX: 0,
    offsetY: 2
} : null)  // 选中时添加阴影

这段代码为选中的列表项添加了更丰富的视觉效果,包括圆角、边框和阴影。

六、列表项交互优化

1. 点击效果优化

// 添加点击效果
.stateStyles({
    pressed: {
        opacity: 0.7,
        backgroundColor: '#F5F5F5'
    },
    normal: {
        opacity: 1,
        backgroundColor: this.selectedIds.includes(file.id) ? '#F0F7FF' : '#FFFFFF'
    }
})

这段代码使用stateStyles为列表项添加了按下状态的样式,提供更好的点击反馈。

2. 滑动操作

除了选择模式,我们还可以为列表项添加滑动操作功能:

// 在ListItem中添加滑动操作
.swipeAction({
    end: [
        {
            action: () => {
                // 删除单个文件
                this.files = this.files.filter(f => f.id !== file.id)
            },
            backgroundcolor: '#F44336',
            label: '删除'
        }
    ],
    start: [
        {
            action: () => {
                // 收藏文件
                console.info(`收藏文件:${file.name}`)
            },
            backgroundcolor: '#FFC107',
            label: '收藏'
        }
    ]
})

这段代码为列表项添加了滑动操作功能,用户可以通过左右滑动列表项来快速执行删除或收藏操作。

七、状态持久化

在实际应用中,我们可能需要在用户离开页面后保持选择状态,或者在应用重启后恢复选择状态。

1. 使用AppStorage保存状态

// 在组件中使用AppStorage
@StorageLink('selectedFileIds') selectedIds: number[] = []
@StorageLink('isInSelectMode') isSelectMode: boolean = false

// 在组件销毁前保存状态
aboutToDisappear() {
    AppStorage.SetOrCreate('selectedFileIds', this.selectedIds)
    AppStorage.SetOrCreate('isInSelectMode', this.isSelectMode)
}

这段代码使用@StorageLink装饰器和AppStorage来保存和恢复选择状态。

2. 使用持久化存储

对于需要在应用重启后仍然保持的状态,可以使用持久化存储:

import dataPreferences from '@ohos.data.preferences'

// 保存状态到持久化存储
async saveSelectionState() {
    try {
        const preferences = await dataPreferences.getPreferences(this.context, 'FileManagerPrefs')
        await preferences.put('selectedFileIds', JSON.stringify(this.selectedIds))
        await preferences.put('isSelectMode', this.isSelectMode)
        await preferences.flush()
    } catch (error) {
        console.error(`保存状态失败: ${error}`)
    }
}

// 从持久化存储恢复状态
async loadSelectionState() {
    try {
        const preferences = await dataPreferences.getPreferences(this.context, 'FileManagerPrefs')
        const selectedIdsStr = await preferences.get('selectedFileIds', '[]')
        const isSelectMode = await preferences.get('isSelectMode', false)
        
        this.selectedIds = JSON.parse(selectedIdsStr as string)
        this.isSelectMode = isSelectMode as boolean
    } catch (error) {
        console.error(`加载状态失败: ${error}`)
    }
}

八、高级列表优化

1. 分类显示与过滤

我们可以添加按文件类型分类显示和过滤的功能:

// 文件类型过滤
@State currentFilter: string = 'all'  // 'all', 'document', 'image', 'video', etc.

// 获取过滤后的文件列表
get filteredFiles() {
    if (this.currentFilter === 'all') {
        return this.files
    }
    
    return this.files.filter(file => {
        switch (this.currentFilter) {
            case 'document':
                return ['docx', 'xlsx', 'pptx', 'pdf'].includes(file.type)
            case 'image':
                return ['jpg', 'png', 'gif'].includes(file.type)
            case 'video':
                return ['mp4', 'mov'].includes(file.type)
            default:
                return true
        }
    })
}

// 在UI中添加过滤选项卡
Row() {
    ForEach(['all', 'document', 'image', 'video'], (filter) => {
        Text(this.getFilterName(filter))
            .fontSize(14)
            .fontColor(this.currentFilter === filter ? '#007DFF' : '#666666')
            .backgroundColor(this.currentFilter === filter ? '#E6F2FF' : 'transparent')
            .padding(8)
            .borderRadius(16)
            .margin({ right: 8 })
            .onClick(() => {
                this.currentFilter = filter
            })
    })
}
.width('100%')
.padding(8)
.scrollable(ScrollDirection.Horizontal)

// 获取过滤器显示名称
getFilterName(filter: string): string {
    switch (filter) {
        case 'all': return '全部'
        case 'document': return '文档'
        case 'image': return '图片'
        case 'video': return '视频'
        default: return filter
    }
}

2. 搜索功能

添加搜索功能,允许用户快速查找文件:

// 搜索关键字
@State searchKeyword: string = ''

// 获取搜索结果
get searchResults() {
    if (!this.searchKeyword) {
        return this.filteredFiles
    }
    
    const keyword = this.searchKeyword.toLowerCase()
    return this.filteredFiles.filter(file => 
        file.name.toLowerCase().includes(keyword) ||
        file.type.toLowerCase().includes(keyword)
    )
}

// 在UI中添加搜索框
Row() {
    Image($r('app.media.search_icon'))
        .width(20)
        .height(20)
        .margin({ right: 8 })
    
    TextInput({ placeholder: '搜索文件', text: this.searchKeyword })
        .width('100%')
        .height(40)
        .backgroundColor('transparent')
        .onChange((value) => {
            this.searchKeyword = value
        })
}
.width('100%')
.padding({ left: 16, right: 16, top: 8, bottom: 8 })
.backgroundColor('#F5F5F5')
.borderRadius(20)
.margin({ top: 8, bottom: 8, left: 16, right: 16 })

3. 拖拽排序

实现拖拽排序功能,允许用户重新排列文件:

// 在List中启用拖拽排序
.dragable(true)
.onDragStart((event: DragEvent, extraParams: string) => {
    // 拖拽开始时的处理
    const index = parseInt(extraParams)
    return this.files[index]
})
.onDrop((event: DragEvent, extraParams: string) => {
    // 拖拽结束时的处理
    const dragIndex = parseInt(extraParams.split(',')[0])
    const dropIndex = parseInt(extraParams.split(',')[1])
    
    // 重新排序文件列表
    const draggedItem = this.files[dragIndex]
    this.files.splice(dragIndex, 1)
    this.files.splice(dropIndex, 0, draggedItem)
    
    return true
})

九、代码结构与样式设置

功能模块 主要实现方法 关键属性和样式
长按触发选择 LongPressGesture onAction回调
批量操作 deleteSelectedFiles, moveSelectedFiles, shareSelectedFiles AlertDialog, 过滤操作
动画效果 animation, transition duration, curve, opacity, scale, translate
自定义样式 Checkbox样式, ListItem样式 selectedColor, borderRadius, shadow
交互优化 stateStyles, swipeAction pressed状态, 滑动操作
状态持久化 AppStorage, dataPreferences StorageLink, getPreferences
分类与过滤 filteredFiles计算属性 过滤逻辑, UI实现
搜索功能 searchResults计算属性 TextInput, 搜索逻辑
拖拽排序 dragable, onDragStart, onDrop 拖拽事件处理

十、总结

本教程详细讲解了HarmonyOS NEXT中可选择列表的进阶功能和优化技巧, 通过这些进阶功能,你可以构建一个功能丰富、用户体验出色的可选择列表应用。这些技巧不仅适用于文件管理应用,也可以应用到其他需要多选功能的场景,如购物车、照片选择器、任务管理等。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐