146.[HarmonyOS NEXT 实战案例七 :List系列] 可选择列表进阶篇
[HarmonyOS NEXT 实战案例七 :List系列] 可选择列表进阶篇
项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star
效果演示
在基础篇中,我们学习了如何实现一个基本的可选择列表。本篇教程将深入探讨可选择列表的进阶功能和优化技巧,帮助你构建更加强大、用户体验更佳的可选择列表应用。
一、进阶功能概述
在本教程中,我们将探讨以下进阶功能:
- 长按触发选择模式:通过长按列表项进入选择模式
- 批量操作功能实现:删除、移动、分享等批量操作的具体实现
- 选择状态的动画效果:为选择状态变化添加流畅的动画效果
- 自定义选择样式:定制选择框和选中状态的视觉效果
- 列表项交互优化:优化列表项的点击、长按等交互体验
- 状态持久化:保存和恢复选择状态
二、长按触发选择模式
除了通过按钮进入选择模式外,我们还可以实现长按列表项进入选择模式的功能,这是移动应用中常见的交互模式。
// 在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
}
}
}
})
}
这个方法实现了批量删除功能:
- 弹出确认对话框
- 用户确认后,过滤掉选中的文件
- 清空选中列表
- 如果删除后没有文件了,退出选择模式
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中可选择列表的进阶功能和优化技巧, 通过这些进阶功能,你可以构建一个功能丰富、用户体验出色的可选择列表应用。这些技巧不仅适用于文件管理应用,也可以应用到其他需要多选功能的场景,如购物车、照片选择器、任务管理等。
- 0回答
- 4粉丝
- 0关注
- 145.[HarmonyOS NEXT 实战案例七 :List系列] 可选择列表基础篇
- 148.[HarmonyOS NEXT 实战案例八 :List系列] 粘性头部列表进阶篇
- 156.[HarmonyOS NEXT 实战案例十二 :List系列] 聊天消息列表 - 进阶篇
- 138.[HarmonyOS NEXT 实战案例七:List系列] 多列列表组件实战:打造精美应用推荐页 进阶篇
- 136.[HarmonyOS NEXT 实战案例七:List系列] 水平列表组件实战:打造精美图片库 进阶篇
- 154.[HarmonyOS NEXT 实战案例十一 :List系列] 自定义内容列表 - 进阶篇
- 134.[HarmonyOS NEXT 实战案例六:List系列] 垂直列表组件实战:打造高效联系人列表 进阶篇
- 142.[HarmonyOS NEXT 实战案例九:List系列] 分组列表组件实战:打造分类设置菜单 进阶篇
- 152.[HarmonyOS NEXT 实战案例十二:List系列] 卡片样式列表组件实战:打造精美电商应用 进阶篇
- 144.[HarmonyOS NEXT 实战案例十:List系列] 字母索引列表组件实战:打造高效联系人应用 进阶篇
- 140.[HarmonyOS NEXT 实战案例八:List系列] 滑动操作列表组件实战:打造高效待办事项应用 进阶篇
- 150.[HarmonyOS NEXT 实战案例十一:List系列] 下拉刷新和上拉加载更多列表组件实战:打造高效新闻应用 进阶篇
- 137.[HarmonyOS NEXT 实战案例七:List系列] 多列列表组件实战:打造精美应用推荐页 基础篇
- 135.[HarmonyOS NEXT 实战案例七:List系列] 水平列表组件实战:打造精美图片库 基础篇
- 147.[HarmonyOS NEXT 实战案例八 :List系列] 粘性头部列表基础篇