ITPub博客

首页 > 移动开发 > ANDROID > HarmonyOS(鸿蒙)——滑动事件

HarmonyOS(鸿蒙)——滑动事件

ANDROID 作者:只要快乐 时间:2025-12-04 10:34:45 0 删除 编辑

基础滑动操作实现

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/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2022-03-18

  • 博文量
    14
  • 访问量
    2744