鸿蒙-下拉刷新、上拉加载更多的 RefreshList

295 阅读2分钟

鸿蒙的Component不支持泛型。。。

鸿蒙的组件不支持泛型,有两种方案解决

  • 拿Object承接,然后使用的时候,强转回来
  • 使用联合类型,但是前提得知道所有的类型(放弃)

1724390607438.gif 1724390601436.gif

自定义组件 RefreshList

  • 下拉刷新我们可以使用Refresh
  • 上拉更多,有两种方案,推荐使用第二种,可以更快的现实UI,加载时机更快
    • 使用 onReachEnd() 监听到滑动到底部
    • 使用 onScrollIndex 监听滑动到倒数第二个,或者 第三个的时候,开始加载更多
    • 使用 Item 的 onAppear监听当前index是 倒数第二个,或者 第三个的时候,开始加载更多

下拉刷新

自定义RefreshList,由于Component不支持泛型,并且next版本不能写any,暂时写string[] 数组 先定义一个Component

@Component
@Preview
export struct RefreshList {
}

首先想一下我们的刷新都需要哪些参数

  1. 加载更多的回调

  2. 刷新的时候回调

  3. 是否正在刷新

  4. 加载更多的枚举

    • 初始化
    • 加载更多中
    • 加载失败
    • 加载结束
    • 加载完成(没有更多的数据了)
  5. listItem ,外界的listItem

  6. 刷新的文案

    • 刷新成功
    • 正在刷新。。。
    • 继续下拉刷新
    • 松手开始刷新
@Component
@Preview
export struct  RefreshList  {
  // 数据源
  @Watch('onDataChange')
  @Link data: Object[]
  @Require scroller: Scroller = new Scroller()
  // 加载更多的回调
  @Require onLoadMore: () => void = () => {
  }
  // 刷新的时候回调
  @Require onRefresh: () => void = () => {
  }
  // 是否正在刷新
  @Link isRefresh: boolean
  // 加载更多的枚举
  @Watch('onLoadMoreStateChange')
  @Link loadMoreState: LoadMoreState

  // list 单个item
  @BuilderParam listItem: (item: Object, index: number) => void
  // 刷新的文案
  @State refreshText: string = '加载中。。。'
  @State loadingText: string = '正在加载。。。'
  // 刷新文案左边的图片
  @State refreshIcon: PixelMap | ResourceStr | DrawableDescriptor = $r("app.media.arrow_down")
}

自定义 Refresh的builder

// 下拉刷新的自定义builder
@Builder
refreshBuild() {
  Row({ space: 10 }) {
    if (this.isRefresh) {
      Progress({ value: 0, total: 100, type: ProgressType.Ring })
        .width(40).color(Color.Gray)
        .style({ strokeWidth: 5, status: ProgressStatus.LOADING })
    } else {
      Image(this.refreshIcon).width(50).height(50)
    }
    // 刷新的文案
    Text(this.refreshText)
  }.alignItems(VerticalAlign.Center)
}

Reresh的逻辑

Refresh({ refreshing: $$this.isRefresh, builder: this.refreshBuild }) {
  List({ scroller: this.scroller }) {
    ForEach(this.data, (item: Object, index: number) => {
      ListItem() {
        this.listItem(item, index)
      }
    }, (item: Object) => item)

    this.loadMore()
  }
  .onScrollIndex((start: number, end: number, center: number) => {
    // 有子组件划入或划出List显示区域时触发。从API version 10开始,List显示区域中间位置子组件变化时也会触发
    if (end >= this.data.length - 1) {
      // 显示加载
      console.log(`end = ${end} length = ${this.data.length} this.loadMoreState=>${this.loadMoreState}`)
      // 如果是初始化状态,或者 加载更多的时候,进去刷新
      if (this.loadMoreState == LoadMoreState.LoadFinish || this.loadMoreState == LoadMoreState.None) {
        this.loadMoreState = LoadMoreState.Loading
        this.onLoadMore()
      }
    }
  })
  .width('100%')
  .height('100%')
}
.onStateChange((refreshStatus: RefreshStatus) => {
  switch (refreshStatus) {
    case RefreshStatus.Inactive:
    // 默认未下拉状态
      break
    case RefreshStatus.Drag:
    //下拉中,下拉距离小于刷新距离。
      this.refreshText = '继续下拉加载'
      this.refreshIcon = $r("app.media.arrow_down")

      break
    case RefreshStatus.OverDrag:
    // 下拉中,下拉距离超过刷新距离。
      this.refreshText = '松手即可加载'
      this.refreshIcon = $r("app.media.arrow_up")
      break
    case RefreshStatus.Refresh:
    //下拉结束,回弹至刷新距离,进入刷新状态。
      this.refreshText = '加载中。。。'
      break
    case RefreshStatus.Done:
    // 刷新结束,返回初始状态(顶部)。
    //下拉结束,回弹至刷新距离,进入刷新状态。
      this.refreshText = '成功'
      this.refreshIcon = $r("app.media.refresh_success")
      break
  }
  console.info('Refresh onStatueChange state is ' + refreshStatus)
})
.onRefreshing(this.onRefresh)
.refreshOffset(OFFSET)

loadmore组件

@Builder
loadMore() {
  ListItem() {
    Row() {
      // 如果是正在加载更多显示个loading
      if (this.loadMoreState == LoadMoreState.Loading) {
        LoadingProgress().width(30).height(30)
      }
      // 加载更多的文案
      Text(this.loadingText)
        .fontSize(20)
        .align(Alignment.Center)
        .height(100)
        .textAlign(TextAlign.Center)
    }
    .justifyContent(FlexAlign.Center)
    .alignItems(VerticalAlign.Center)
    .width('100%')
  }
}

使用

import { LoadMoreState, RefreshList } from '../../component/RefreshList';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct ListDemo {
  @State message: string = 'Hello World';
  @State data: string[] = []
  @State isRefresh: boolean = false
  @State isLoadMore: boolean = false
  @State loadMoreState: LoadMoreState = LoadMoreState.None
  scroller: Scroller = new Scroller()
  index: number = 0

  aboutToAppear(): void {
    for (let index = 0; index < 15; index++) {
      // this.data.push(`${Math.floor(Math.random() * 100)}`)
      this.data.push(index.toString())
    }
    console.error('onPageShow')

  }

  @Builder
  listItem(item: Object, index: number) {
    Text(item as string)
      .fontSize(30)
      .align(Alignment.Center)
      .height(100)
      .width('100%')
      .textAlign(TextAlign.Center)
      .onClick(() => {
        promptAction.showToast({ message: `点击的位置是:${index} , 内容是:${item}` })
      })
  }

  build() {
    RelativeContainer() {
      RefreshList({
        data: this.data,
        isRefresh: this.isRefresh,
        loadMoreState: this.loadMoreState,
        scroller: this.scroller,
        onLoadMore: () => {
          return new Promise<string>((resolve, reject) => {
            // 模拟网络请求操作
            setTimeout(() => {
              resolve('加载更多成功');
              console.log(`执行加载更多`)
              let arr2 = [`${this.index++}`, `${this.index++}`, `${this.index++}`, `${this.index++}`, `${this.index++}`]
              this.data.push(...arr2)
              this.isLoadMore = false
              if (this.index > 9) {
                this.loadMoreState = LoadMoreState.LoadEnd
              } else {
                this.loadMoreState = LoadMoreState.LoadFinish
              }
            }, 2000);
          });
        },
        onRefresh: () => {
          return new Promise<string>((resolve, reject) => {
            // 模拟网络请求操作
            setTimeout(() => {
              resolve('刷新成功');
              promptAction.showToast({ message: '刷新成功' })
              this.data = this.getData();
              this.isRefresh = false

            }, 2000);
          });
        },
        listItem: this.listItem
      }).height('100%').width('100%')
    }
    .height('100%')
    .width('100%')
    .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
  }

  getData(): string[] {
    let addData: string[] = []
    for (let index = 0; index < 20; index++) {
      addData.push(`${Math.floor(Math.random() * 100)}`)
    }
    return addData;
  }
}

源码位置