HarmonyOS 鸿蒙Next中如何实现上拉加载更多

HarmonyOS 鸿蒙Next中如何实现上拉加载更多

如何实现上拉加载更多

4 回复

【背景知识】

  • PullToRefresh是一款OpenHarmony环境下可用的下拉刷新、上拉加载组件。支持设置内置动画的各种属性,支持设置自定义动画,支持lazyForEarch的数据作为数据源。
    PullToRefresh使用限制:
    1、目前只支持List、Scroll、Tabs、Grid和WaterFlow系统容器组件;
    2、暂不支持设置系统容器组件的弹簧效果和阴影效果,使用时需要将系统组件edgeEffect属性的值设置为(EdgeEffect.None);
    3、暂不支持页面触底时自动触发上拉加载功能;
    4、暂不支持在页面数据不满一屏时触发上拉加载功能;
    5、暂不支持通过代码的方式去触发下拉刷新功能;
    6、暂不支持在下拉刷新动画结束时提供手势结束的回调。

  • Refresh:可以进行页面下拉操作并显示刷新动效的容器组件。

  • List组件:列表包含一系列相同宽度的列表项。

  • Button组件:按钮组件通常用于响应用户的点击操作,其类型包括胶囊按钮、圆形按钮、普通按钮、圆角矩形按钮。Button做为容器使用时可以通过添加子组件实现包含文字、图片等元素的按钮。

  • $$运算符:$$运算符为系统组件提供TS变量的引用,使得TS变量和系统组件的内部状态保持同步。

【解决方案】

场景一解决方案:
在项目根目录下的oh-package.json5文件中配置PullToRefresh:

{
  // ...
  "devDependencies": {
    "[@ohos](/user/ohos)/pulltorefresh": "2.1.2"
  }
}

在模块目录下的oh-package.json5文件中配置PullToRefresh:

{
  "name": "entry",
  // ...
  "dependencies": {
    "[@ohos](/user/ohos)/pulltorefresh": "^2.1.2"
  }
}

示例代码实现:

import { PullToRefresh } from '[@ohos](/user/ohos)/pulltorefresh'

@Entry
@Component
struct ScrollCeilingTab {
  scroller: Scroller = new Scroller()
  private tabscroller: Scroller = new Scroller();
  @State itemData: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  tabTitles: Array<string> = ['Tab1', 'Tab2', 'Tab3']

  @Builder
  private getListView() {
    List({ space: 20, scroller: this.tabscroller }) {
      ForEach(this.itemData, (item: number) => {
        ListItem() {
          Text(`${item}`)
            .height(80)
            .width('100%')
            .textAlign(TextAlign.Center)
            .backgroundColor(0xDDDDDD)
            .margin({ bottom: 5 })
        }
      })
    }
    .nestedScroll({
      scrollForward: NestedScrollMode.PARENT_FIRST,
      scrollBackward: NestedScrollMode.SELF_FIRST
    })
    .backgroundColor('#eeeeee')
    .divider({ strokeWidth: 1, color: 0x222222 })
    .edgeEffect(EdgeEffect.None) // 必须设置列表为滑动到边缘无效果
  }

  @Builder
  tabContentData(tabTitle: string) {
    TabContent() {
      Column() {
        PullToRefresh({
          data: this.itemData,
          scroller: this.tabscroller,
          customList: () => {
            this.getListView();
          },
          // 可选项,下拉刷新回调
          onRefresh: () => {
            return new Promise<string>((resolve, reject) => {
              // 模拟网络请求操作,请求网络2秒后得到数据,通知组件,变更列表数据
              setTimeout(() => {
                resolve('刷新成功');
                let num = this.itemData.length
                this.itemData.push(num);
              }, 500);
            });
          },
          // 可选项,上拉加载更多回调
          onLoadMore: () => {
            return new Promise<string>((resolve, reject) => {
              setTimeout(() => {
                resolve('');
                let num = this.itemData.length
                this.itemData.push(num);
              }, 2000);
            });
          },
          customLoad: null,
          customRefresh: null,
        })
      }
    }
    .tabBar(tabTitle)
    .padding({ top: 5, bottom: 5 })
    .borderWidth(1)
    .borderColor(Color.Red)
  }

  build() {
    Column({ space: 10 }) {
      Scroll(this.scroller) {
        Column() {
          Image($r('app.media.startIcon')).height(70)
          Tabs() {
            ForEach(this.tabTitles, (title: string) => {
              this.tabContentData(title)
            })
          }.borderWidth(2)
        }.width('90%').alignItems(HorizontalAlign.Center)
      }.width('100%').align(Alignment.Center).scrollBar(BarState.Off)
    }
  }
}
  • 场景二解决方案:
    可以使用$$双向绑定Refresh组件参数refreshing,点击按钮手动控制组件刷新状态实现,示例代码如下:
@Entry
@Component
struct RefreshExample {
  @State isRefreshing: boolean = false
  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']

  build() {
    Column() {
      Row() {
        Button('开始刷新')
          .onClick(() => {
            // 手动开始刷新
            this.isRefreshing = true
          })
        Button('结束刷新')
          .onClick(() => {
            // 手动停止刷新
            this.isRefreshing = false
          })
      }
      Refresh({ refreshing: $$this.isRefreshing }) {
        List() {
          ForEach(this.arr, (item: string) => {
            ListItem() {
              Text('' + item)
                .width('70%')
                .height(80)
                .fontSize(16)
                .margin(2)
                .borderRadius(10)
                .backgroundColor(0xFFFFFF)
            }
          }, (item: string) => item)
        }
      }
      .onRefreshing(() => {
        // 2秒后自动结束刷新
        setTimeout(() => {
          this.isRefreshing = false
        }, 2000)
      })
      .backgroundColor(0x89CFF0)
    }
  }
}
  • 场景三解决方案:
    可以在外层List使用Refresh组件实现上拉刷新,内层List使用pulltofresh进行下拉加载,代码示例如下:
import { PullToRefresh, PullToRefreshConfigurator } from '[@ohos](/user/ohos)/pulltorefresh'

const tabsList = ["能源", "化工", "塑料", "化纤", "聚氨酯"]

@Component
struct Index {
  private refreshScroller: Scroller = new Scroller();
  private refreshConfigurator: PullToRefreshConfigurator = new PullToRefreshConfigurator();
  @State list: Array<string> =
    ["a", "b", "c", "d", "e", "f", "a1", "b1", "c1", "d1", "e1", "f1"]

  aboutToAppear(): void {
    this.refreshConfigurator.setHasRefresh(false);
  }

  @Builder
  getListView() {
    List({ space: 10, scroller: this.refreshScroller }) {
      ForEach(this.list,
        (item: string, idx: number) => {
          ListItem() {
            Text(item)
              .width("100%")
              .height(200)
              .width('100%')
              .backgroundColor("#ffe0dbdb")
          }
        })
    }
    .height('100%')
    .width("100%")
    .scrollBar(BarState.Off)
    .edgeEffect(EdgeEffect.None)
    .nestedScroll({
      scrollForward: NestedScrollMode.PARENT_FIRST,
      scrollBackward: NestedScrollMode.SELF_FIRST
    })
  }

  build() {
    Column({ space: 10 }) {
      PullToRefresh({
        // 必传项,列表组件所绑定的数据
        data: $list,
        refreshConfigurator: this.refreshConfigurator,
        // 必传项,需绑定传入主体布局内的列表或宫格组件
        scroller: this.refreshScroller,
        // 必传项,自定义主体布局,内部有列表或宫格组件
        customList: () => {
          // 一个用@Builder修饰过的UI方法
          this.getListView();
        },
        // 可选项,上拉加载更多回调
        onLoadMore: () => {
          return new Promise<string>((resolve, reject) => {
            // 模拟网络请求操作,请求网络2秒后得到数据,通知组件,变更列表数据
            setTimeout(() => {
              resolve('加载更多啦啦啦');
              this.list.push('a2');
              this.list.push('b2');
            }, 2000);
          });
        },
        customLoad: null,
        customRefresh: null,
      }).width('100%').height('100%')
    }.width('100%')
  }
}

@Entry
@Component
struct KeyboadPage {
  private refreshScroller: Scroller = new Scroller();
  @State isRefresh: boolean = false;

  build() {
    Column({ space: 10 }) {
      Refresh({ refreshing: $$this.isRefresh, builder: this.buildRefreshCom() }) {
        this.getListView()
      }.onRefreshing(() => {
        setTimeout(() => {
          this.isRefresh = false;
        }, 1000)
      })
    }
    .width('100%')
    .height('100%')
  }

  @Builder
  buildRefreshCom() {
    Row() {
      LoadingProgress().height(32)
      Text("正在刷新...").fontSize(16).margin({
        left: 20,
      })
    }
    .alignItems(VerticalAlign.Center)
  }

  @Builder
  getListView() {
    List({ scroller: this.refreshScroller }) {
      ListItem() {
        Text("第一条")
          .width("100%")
          .height(300)
          .backgroundColor("#ffe0dbdb")
      }

      ListItem() {
        Text("第二条")
          .width("100%")
          .height(500)
          .backgroundColor("#ff74a6ec")
      }

      ListItem() {
        Text("第三条")
          .width("100%")
          .height(200)
          .backgroundColor("#ffdbdfe0")
      }

      ListItem() {
        Tabs({
          barPosition: BarPosition.Start,
        }) {
          ForEach(tabsList,
            (item: string, idx: number) => {
              TabContent() {
                Index()
              }
              .tabBar(this.watchingTabBuilder(idx, item))
            })
        }
        .width("100%")
        .barHeight(50)
        .barMode(BarMode.Scrollable)
        .onChange((index: number) => {
        })
      }
    }
    .edgeEffect(EdgeEffect.None)
    .height('100%')
    .width('100%')
    .scrollBar(BarState.Off)
  }

  @Builder
  watchingTabBuilder(index: number, item: string) {
    Text(item)
      .fontSize(16)
      .fontColor("#121212")
      .padding({
        left: 15,
        right: 15,
        top: 12,
        bottom: 12
      })
      .borderRadius(5)
  }
}

更多关于HarmonyOS 鸿蒙Next中如何实现上拉加载更多的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


滚动容器的onScrollEdge可以触发触底回调

.onScrollEdge((side: Edge) => {
console.info('To the edge');
})

在HarmonyOS Next中实现上拉加载更多,可通过List组件的onReachEnd事件监听滚动到底部。当触发该事件时,执行加载数据的逻辑。示例代码:

List() {
  // 列表内容
}
.onReachEnd(() => {
  // 加载更多数据
})

需结合数据源状态管理,避免重复加载。

在HarmonyOS Next中,可以通过以下方式实现上拉加载更多功能:

  1. 使用ListContainer组件:结合OnScrollListener监听滚动事件,当滚动到底部时触发数据加载
  2. 自定义Refresh组件:通过Component.ScrolledListener监听滑动距离,判断是否达到加载阈值
  3. 使用ArkUI声明式开发
@State listData: string[] = [...]
@State isLoading: boolean = false

List() {
  ForEach(this.listData, (item: string) => {
    ListItem() {
      Text(item)
    }
  })
  // 加载更多指示器
  if (this.isLoading) {
    ListItem() {
      Progress()
    }
  }
}
.onReachEnd(() => {
  if (!this.isLoading) {
    this.loadMoreData()
  }
})

关键实现要点:

  • 设置合理的加载阈值(如距离底部50px)
  • 添加加载状态防止重复请求
  • 配合分页参数实现数据增量加载
  • 考虑网络异常等边界情况处理

建议结合@ohos.net.http模块实现数据请求,使用@State管理加载状态,确保流畅的用户体验。

回到顶部