首页 > 移动开发 > ANDROID > HarmonyOS(鸿蒙)——滑动事件
基础滑动操作实现
SwipeAction组件基础
HarmonyOS通过SwipeAction组件为列表项提供滑动操作能力。与简单的滑动删除不同,SwipeAction支持左右两侧的多操作按钮,且具有丰富的自定义选项。
@Entry @Component struct SwipeActionSample { private tasks: Task[] = [ { id: 1, title: '完成项目文档', completed: false }, { id: 2, title: '技术方案评审', completed: true }, { id: 3, title: '团队周会', completed: false } ] build() { List({ space: 10 }) { ForEach(this.tasks, (task: Task) => { ListItem() { SwipeAction({ builder: this.ActionBuilder, onAction: (action: SwipeActionAction) => { this.handleAction(task, action) } }) { // 主内容区域 this.ContentBuilder(task) } } }, (task: Task) => task.id.toString()) } .width('100%') .height('100%') } @Builder ActionBuilder() { Row({ space: 0 }) { // 左侧滑动操作 Button('置顶') .backgroundColor('#FFA500') .width(80) .onClick(() => { // 处理置顶操作 }) // 右侧滑动操作 Button('删除') .backgroundColor('#FF4500') .width(80) .onClick(() => { // 处理删除操作 }) } } @Builder ContentBuilder(task: Task) { Row({ space: 10 }) { Image(task.completed ? $r('app.media.checked') : $r('app.media.unchecked')) .width(20) .height(20) Text(task.title) .fontSize(16) .fontColor(task.completed ? '#999' : '#000') .decoration({ type: task.completed ? TextDecorationType.LineThrough : TextDecorationType.None }) } .padding(10) .backgroundColor('#FFF') .borderRadius(8) } private handleAction(task: Task, action: SwipeActionAction) { // 处理滑动操作回调 } }
滑动方向与阈值控制
SwipeAction支持精确控制滑动方向和触发阈值,这对于不同场景下的用户体验优化至关重要。
SwipeAction({
builder: this.CustomActionBuilder,
edgeEffect: SwipeEdgeEffect.Spring, // 边缘弹性效果
onAction: this.handleCustomAction
})
.direction(SwipeDirection.EndToStart) // 只允许从右向左滑动
.threshold(0.3) // 滑动超过30%宽度时触发操作
.friction(0.6) // 滑动摩擦系数,控制滑动流畅度
高级滑动交互实现
多层级滑动操作
在实际应用中,单一层级的滑动操作可能无法满足复杂业务需求。我们可以实现多层级滑动操作,为用户提供更丰富的交互可能。
@Component struct MultiLevelSwipeAction { @State currentLevel: number = 0 @State offsetX: number = 0 build() { Stack({ alignContent: Alignment.Start }) { // 背景层操作 this.BackgroundActions() // 前景层内容 this.ForegroundContent() } .gesture( PanGesture({ direction: PanDirection.Horizontal }) .onActionStart(() => { this.onSwipeStart() }) .onActionUpdate((event: GestureEvent) => { this.onSwipeUpdate(event) }) .onActionEnd(() => { this.onSwipeEnd() }) ) } @Builder BackgroundActions() { Row({ space: 0 }) { // 第一级操作:常用功能 this.ActionButton('标星', '#FFD700', 0) this.ActionButton('置顶', '#FFA500', 1) // 第二级操作:高级功能 this.ActionButton('归档', '#6495ED', 2) this.ActionButton('删除', '#FF4500', 3) } .width('100%') .justifyContent(FlexAlign.End) } @Builder ForegroundContent() { Row() { // 主内容 Text('多层级滑动操作示例') .fontSize(16) .fontColor(Color.Black) } .width('100%') .height(60) .backgroundColor(Color.White) .offset({ x: this.offsetX }) } @Builder ActionButton(text: string, color: string, level: number) { Button(text) .backgroundColor(color) .width(80 * (level + 1)) // 不同层级按钮宽度不同 .height(60) .onClick(() => { this.handleAction(level, text) }) } private onSwipeStart() { // 滑动开始时的初始化 this.currentLevel = 0 } private onSwipeUpdate(event: GestureEvent) { const swipeDistance = event.offsetX const maxSwipeDistance = 320 // 最大滑动距离 // 根据滑动距离计算当前层级 this.currentLevel = Math.floor(Math.abs(swipeDistance) / 80) this.offsetX = Math.max(-maxSwipeDistance, Math.min(0, swipeDistance)) } private onSwipeEnd() { // 根据最终位置确定操作层级 const targetLevel = Math.round(Math.abs(this.offsetX) / 80) this.animateToLevel(targetLevel) } private animateToLevel(level: number) { // 平滑动画过渡到指定层级 animateTo({ duration: 300, curve: Curve.EaseOut }, () => { this.offsetX = -level * 80 this.currentLevel = level }) } }
自定义手势识别与冲突处理
在复杂列表场景中,正确处理手势冲突是保证用户体验的关键。HarmonyOS提供了强大的手势识别和冲突解决机制。
@Component struct AdvancedGestureList { @State items: ListItemData[] = [] private panGesture: PanGesture = new PanGesture({ direction: PanDirection.All }) build() { List({ space: 8 }) { ForEach(this.items, (item: ListItemData, index: number) => { ListItem() { this.CustomSwipeItem(item, index) } }) } .onScrollIndex((start: number, end: number) => { this.handleScrollIndexChange(start, end) }) } @Builder CustomSwipeItem(item: ListItemData, index: number) { let startX: number = 0 let currentX: number = 0 let isSwiping: boolean = false Row() { // 内容区域 this.ItemContent(item) // 滑动操作区域 this.SwipeActions(item) } .gesture( this.panGesture .onActionStart((event: GestureEvent) => { startX = event.offsetX isSwiping = false }) .onActionUpdate((event: GestureEvent) => { currentX = event.offsetX const deltaX = currentX - startX if (Math.abs(deltaX) > 10 && !isSwiping) { isSwiping = true this.onSwipeStart(index) } if (isSwiping) { this.onSwipeUpdate(index, deltaX) } }) .onActionEnd(() => { if (isSwiping) { this.onSwipeEnd(index, currentX - startX) } }) .onActionCancel(() => { this.onSwipeCancel(index) }) ) .simultaneousGesture(this.getNestedGesture(item)) // 处理嵌套手势 } // 处理与父组件的滑动冲突 private getNestedGesture(item: ListItemData): GestureGroup { return GestureGroup(GestureMode.Exclusive, PanGesture({ direction: PanDirection.Vertical }) .onActionUpdate((event: GestureEvent) => { // 垂直滑动时取消水平滑动手势 if (Math.abs(event.offsetY) > Math.abs(event.offsetX)) { this.cancelSwipeGesture() } }) ) } }
性能优化与最佳实践
列表滑动性能优化
在包含大量数据的列表中,滑动操作的性能直接影响用户体验。以下是几种有效的优化策略:
@Component struct OptimizedSwipeList { private largeDataSet: LargeData[] = [] @State visibleRange: [number, number] = [0, 20] build() { LazyForEach(this.largeDataSet, (item: LargeData) => { ListItem() { this.OptimizedSwipeItem(item) } }, (item: LargeData) => item.id) } @Builder OptimizedSwipeItem(item: LargeData) { SwipeAction({ builder: this.EfficientActionBuilder, onAction: this.handleEfficientAction }) { Row() { // 使用缓存图片 CachedImage(item.imageUrl) .width(40) .height(40) .borderRadius(20) Column() { // 文本渲染优化 OptimizedText(item.title) .maxLines(1) .ellipsisMode(TextEllipsisMode.Tail) OptimizedText(item.subtitle) .fontSize(12) .fontColor('#666') .maxLines(2) } .layoutWeight(1) } .padding(12) } .onAppear(() => { this.onItemAppear(item.id) }) .onDisappear(() => { this.onItemDisappear(item.id) }) } // 使用RecyclerView模式复用组件 private reusePool: Map<string, Component> = new Map() @Builder ReusableSwipeAction(item: LargeData) { let component = this.reusePool.get(item.type) if (!component) { component = this.createSwipeComponent(item.type) this.reusePool.set(item.type, component) } component } }
@Component
struct PhysicsBasedSwipe {
@State position: number = 0
@State velocity: number = 0
private physicsEngine: PhysicsEngine = new PhysicsEngine()
build() {
Row() {
this.SwipeContent()
}
.offset({ x: this.position })
.gesture(
PanGesture({})
.onActionUpdate((event: GestureEvent) => {
this.handlePhysicsUpdate(event)
})
.onActionEnd(() => {
this.startMomentumAnimation()
})
)
}
private handlePhysicsUpdate(event: GestureEvent) {
const deltaX = event.offsetX
this.position += deltaX
// 计算瞬时速度
this.velocity = deltaX / event.timeDelta
// 应用物理阻力
const resistance = this.calculateResistance(this.position)
this.position *= resistance
}
private startMomentumAnimation() {
const config: PhysicsAnimationConfig = {
initialVelocity: this.velocity,
friction: 0.95,
tension: 0.1,
mass: 1.0
}
this.physicsEngine.startAnimation(config, (value: number) => {
this.position += value
this.velocity = value
}, () => {
this.onAnimationComplete()
})
}
private calculateResistance(position: number): number {
const maxResistance = 0.3
const resistanceZone = 100
const resistance = 1 - Math.min(Math.abs(position) / resistanceZone, 1) * maxResistance
return resistance
}
}
@Component struct SharedElementTransition { @State selectedItem: ItemData | null = null @State transitionState: TransitionState = TransitionState.Idle build() { Stack() { // 列表视图 this.ListView() // 详情视图(带共享元素过渡) if (this.selectedItem) { this.DetailViewWithTransition() } } } @Builder DetailViewWithTransition() { // 共享元素过渡容器 SharedTransition({ id: `item_${this.selectedItem.id}`, duration: 300 }) .onAppear(() => { this.onDetailAppear() }) .onDisappear(() => { this.onDetailDisappear() }) { Column() { // 详情内容,与列表项有共享元素 this.SharedImage() this.SharedText() } .gesture( PanGesture({ direction: PanDirection.Vertical }) .onActionUpdate((event: GestureEvent) => { this.handleDetailSwipe(event) }) ) } } private handleDetailSwipe(event: GestureEvent) { if (event.offsetY > 50) { // 向下滑动关闭详情 this.closeDetailWithTransition() } } }
实际应用场景与案例分析
邮件客户端滑动操作
以邮件客户端为例,展示复杂业务场景下的滑动操作实现:
@Component struct EmailSwipeActions { private emails: EmailItem[] = [] build() { List() { ForEach(this.emails, (email: EmailItem) => { ListItem() { SwipeAction({ builder: this.EmailActionBuilder.bind(this, email), onAction: this.handleEmailAction.bind(this, email) }) { this.EmailItemContent(email) } } }) } } @Builder EmailActionBuilder(email: EmailItem) { Row({ space: 0 }) { // 左侧操作:归档、标记 if (!email.archived) { Button($r('app.media.archive')) .backgroundImage($r('app.media.archive_bg')) .width(70) .onClick(() => this.archiveEmail(email)) } Button(email.starred ? $r('app.media.unstar') : $r('app.media.star')) .backgroundImage($r('app.media.star_bg')) .width(70) .onClick(() => this.toggleStar(email)) // 右侧操作:删除、标记未读 Button(email.read ? $r('app.media.mark_unread') : $r('app.media.mark_read')) .backgroundImage($r('app.media.read_bg')) .width(70) .onClick(() => this.toggleRead(email)) Button($r('app.media.delete')) .backgroundImage($r('app.media.delete_bg')) .width(70) .onClick(() => this.deleteEmail(email)) } } @Builder EmailItemContent(email: EmailItem) { Row({ space: 12 }) { // 发件人头像和重要程度指示器 this.SenderSection(email) // 邮件内容摘要 this.EmailPreview(email) // 时间和附件指示器 this.MetaInfo(email) } .padding(16) .backgroundColor(email.read ? '#f8f9fa' : '#ffffff') .border({ width: email.important ? 2 : 0, color: '#ff6b6b' }) } }
// 滑动操作测试用例
describe('SwipeAction Tests', () => {
it('should trigger delete action when swiped beyond threshold', async () => {
const testBed = new SwipeActionTestBed()
await testBed.setupTestList()
// 模拟滑动手势
await testBed.simulateSwipe({
startX: 100,
startY: 200,
endX: 20,
endY: 200,
duration: 300
})
// 验证删除操作被触发
expect(testBed.getDeletedItemsCount()).toBe(1)
})
it('should cancel swipe when vertical scroll detected', async () => {
const testBed = new SwipeActionTestBed()
// 模拟冲突手势
await testBed.simulateConflictingGesture({
horizontalSwipe: { distance: 50, duration: 200 },
verticalScroll: { distance: 30, duration: 100 }
})
// 验证滑动操作被取消
expect(testBed.isSwipeActive()).toBeFalse()
})
})
总结
HarmonyOS的列表项滑动操作提供了强大而灵活的交互能力,从基础的滑动删除到复杂的多层级操作,都能通过系统提供的组件和API实现。本文深入探讨了:
基础实现:使用SwipeAction组件快速实现标准滑动操作
高级交互:多层级滑动、自定义手势识别和冲突处理
性能优化:内存管理、组件复用和渲染优化策略
动画效果:物理动画和共享元素过渡的高级用法
实际应用:复杂业务场景下的最佳实践
通过合理运用这些技术和策略,开发者可以为用户创造流畅、直观且高效的列表交互体验。随着HarmonyOS生态的不断发展,列表滑动操作的能力还将继续扩展和优化,为应用开发提供更多可能性
-----------------------------------
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70015403/viewspace-3107384/ ,如需转载,请注明出处,否则将追究法律责任。