下拉刷新是移动应用常用功能。虽然 ArkUI 自带 Refresh 组件,但在与 List 结合时往往难以满足定制化需求。本文以 RefreshList 为例,介绍如何手动处理触摸事件与滚动冲突,打造高可定制的下拉刷新控件。

1. 布局与插槽拆分

RefreshList 分为两部分:

  • 刷新头(Header):位于可视区域上方,根据下拉距离动态显示提示。
  • 内容区(Content):内部为 List,展示数据项。

使用 @BuilderParam 让调用者传入 itemLayout,自定义每个列表项:

@Component struct RefreshList {
  @BuilderParam itemLayout?: (item: any, index: number) => void;
  @Link dataSet: any[];           // 绑定数据
  @Link refreshing: boolean;      // 触发刷新标志
  onRefresh?: () => void;         // 刷新回调

  @State offsetY: number = -this.headHeight;
  private headHeight = 55;
  private listScroller = new Scroller();

  @Builder headLayout() {
    /* 构建下拉提示头 */
  }

  build() {
    Column() {
      this.headLayout()
      List({ scroller: this.listScroller }) {
        ForEach(this.dataSet, (item, idx) => {
          ListItem() {
            this.itemLayout?.(item, idx);
          }
          .width("100%")
        })
      }
      .onScrollFrameBegin(({ offsetRemain }) => ({ offsetRemain: this.listScrollable ? offsetRemain : 0 }))
      .edgeEffect(EdgeEffect.None)
      .width("100%").layoutWeight(1)
      .position({ x: 0, y: this.offsetY + this.headHeight })
    }
    .width("100%").height("100%")
    .clip(true)
    .onTouch(this.handleTouch.bind(this))
  }

  private handleTouch(event: TouchEvent) { /* Down/Move/Up 逻辑 */ }
}

布局上,通过 position() 动态偏移内容区与头部。

2. 触摸与滚动冲突处理

  • 在组件根 Column 上监听 onTouch

    • Down:记录初始触点。
    • Move:计算垂直位移 deltaY,并在满足下拉条件时改变 offsetY,禁止内部 List 滚动。
    • Up:判断是否达临界点,触发 onRefresh() 并自动回弹或恢复到隐藏状态。
  • List 上使用 .onScrollFrameBegin
    当外层正拖动刷新时,返回 offsetRemain = 0 使 List 暂停滚动;拖动结束后恢复默认行为。

3. 数据与状态联动

  • @Link dataSet:绑定外部数据数组,列表自动渲染。
  • @Link refreshing:由父组件控制刷新开始/结束,内部在监听器中对 UI 做对应动画和提示更新;
  • onRefresh 回调在拖动到位时触发,外部可在该函数内异步拉取新数据,并在完成后将 refreshing 置为 false

4. 使用示例

import { RefreshList } from "./widget/refresh_list";

@Entry @Component struct Demo {
  @State data: string[] = [];
  @State refreshing: boolean = false;

  @Builder itemLayout(item: any, idx: number) {
    Text(`Item ${idx}: ${item}`)
      .width("100%").height(80)
      .textAlign(TextAlign.Center)
      .backgroundColor('#bbccaa')
      .margin({ top: 5 });
  }

  build() {
    Column({ space: 10 }) {
      Button("刷新数据").onClick(() => this.refreshing = true)
      RefreshList({
        dataSet: $data,
        refreshing: $refreshing,
        itemLayout: (i, idx) => this.itemLayout(i, idx),
        onRefresh: () => this.fetchData()
      })
    }
  }

  aboutToAppear() { this.fetchData(); }

  private fetchData() {
    setTimeout(() => {
      this.data = Array.from({ length: 20 }, (_, i) => (Math.random()*100).toFixed(2));
      this.refreshing = false;
    }, 2000);
  }
}

通过上述步骤,你即可获得一个完全自定义、可插拔的下拉刷新列表控件,支持任意列表项渲染与交互反馈。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐