HarmonyOS 鸿蒙Next下实现分页预加载功能
HarmonyOS 鸿蒙Next下实现分页预加载功能
ExperimentalPaging
简介
参考Android paging3的设计,实现了HarmonyOS下的paging分页加载
- 当用户滚动到现有数据的末尾时,自动请求下一页;
- 跟踪获取前一页或后一页所需要的参数;
- 跟踪加载状态,并支持List、Gird、WaterFlow。为失败的加载提供简便的重试功能;
- 支持状态管理V1和状态管理V2
安装
ohpm install @jackiehou/experimental-paging
使用说明
import { LoadParams, LoadResult, LoadState, PagingSource } from '@jackiehou/experimental-paging'
继承PagingSource,如果使用LazyForEach则还需要implements IDataSource接口,使用Repeat则无需implements IDataSource
const PAGE_SIZE = 20 //一页的item的个数
//继承PagingSource并实现LazyForEach的IDataSource接口,如果使用Repeat则无需implements IDataSource
class PagingDataSource extends PagingSource<number, ItemData> implements IDataSource {
private listeners: DataChangeListener[] = []; //如果使用Repeat,则无需使用DataChangeListener
public array: Array<ItemData> = [];
constructor(array?: Array<ItemData>) {
/* 第一个参数 pageSize 一页的个数
* 第二个参数 prefetchDistance 当滑动到末尾/头部小于prefetchDistance个item的时候,触发加载,默认值pageSize乘以2
* 第三个个参数 initialLoadSize 初始化第一页请求的个数,默认值pageSize乘以3
*/
super(PAGE_SIZE, PAGE_SIZE * 2, PAGE_SIZE * 3)
this.array = array ? array : []
}
//PagingSource会调用此方法,在这里需要更新全部数据
reloadData(items: ItemData[]): void {
this.array.splice(0, this.totalCount(), ...items)
this.notifyDataReload() //如果使用Repeat,则无需调用notifyDataReload
}
//PagingSource会调用此方法,在这里添加上、下一页的数据
batchAdd(startIndex: number, items: ItemData[]): void {
this.array.splice(startIndex, 0, ...items)
//如果使用Repeat,则无需调用notifyDatasetChange
this.notifyDatasetChange([{
type: DataOperationType.ADD,
index: startIndex,
count: items.length,
key: items.map(item => JSON.stringify(item))
}])
}
//PagingSource会调用此方法,在这里调用服务器接口获取分页数据
async load(loadParams: LoadParams<number>): Promise<LoadResult<number, ItemData>> {
//当前页的请求参数,如果loadParams.key为undefined表示初始化加载
const key = loadParams.key !== undefined ? loadParams.key : 1 /*初始化从第1页开始加载数据*/
try {
//模拟网络耗时
await sleep(1000)
//根据key和loadParams.loadSize得到当前页的数据
const items = getData((key - 1) * PAGE_SIZE, loadParams.loadSize, key, PAGE_SIZE)
return {
state: LoadState.SUCCEED, //加载成功
prevKey: undefined, //返回上一页的请求的key,undefined表示上一页没有数据
nextKey: key <= 10 ? key + (loadParams.loadSize / PAGE_SIZE) : undefined, //返回下一页的请求的key,undefined表示下一页没有数据
data: items //返回的数据
}
} catch (e){
console.log(`load error key = ${key} error = ${JSON.stringify(e)}`)
return {state: LoadState.ERROR} //返回错误状态
}
}
//PagingSource和IDataSource需要实现的方法,返回总数
totalCount(): number {
return this.array.length
}
//...省略实现LazyForEach的IDataSource接口需要实现的方法,如果使用的是Repeat则不用考虑
}
LazyForEach
@Component
struct sample {
pagingSource = new PagingDataSource()
@State isListAppeared: boolean = false //List组件是否挂载
aboutToAppear(): void {
this.pagingSource.refreshData()
}
aboutToDisappear(): void {
this.pagingSource.release()
}
build() {
Stack() {
if (!this.isListAppeared && this.pagingSource.refresh === LoadState.Loading) {
LoadingProgress()
.color(Color.Blue).width('35%').aspectRatio(1)
} else {
if (this.pagingSource.refresh !== LoadState.ERROR) {
List() {
LazyForEach(this.pagingSource, (item: ItemData) => {
ListItem() {
Row() {
Column()
.height(100)
.width(100)
.backgroundColor(item.color)
Text(item.name).padding(10)
}.width('100%').padding(5).height(110)
}
}, (item: ItemData) => JSON.stringify(item))
}
.size({ width: '100%', height: '100%' })
.padding(15)
.onScrollIndex((start: number, end: number) => {
//在这里必须要调用此方法
this.pagingSource.onVisibleAreaChanged(start, end)
})
.onAppear(() => {
this.isListAppeared = true
})
.onDisAppear(() => {
this.isListAppeared = false
})
} else {
Button('重试').onClick(() => {
this.pagingSource.refreshData()
})
}
}
}.width('100%').height('100%')
}
}
Repeat
@ComponentV2
struct sampleV2 {
@Local array :Array<ItemData> = []
pagingSource = new PagingDataSource(this.array)
@Local isListAppeared: boolean = false //List组件是否挂载
aboutToAppear(): void {
this.pagingSource.refreshData()
}
aboutToDisappear(): void {
this.pagingSource.release()
}
build() {
Stack() {
if (!this.isListAppeared && this.pagingSource.refresh === LoadState.Loading) {
LoadingProgress()
.color(Color.Blue).width('35%').aspectRatio(1)
} else {
if (this.pagingSource.refresh !== LoadState.ERROR) {
List() {
Repeat(this.array)
.each((ri: RepeatItem<ItemData>) => {
ListItem() {
Row() {
Column()
.height(100)
.width(100)
.backgroundColor(ri.item.color)
Text(ri.item.name).padding(10)
}.width('100%').padding(5).height(110)
}
})
.key((item) => JSON.stringify(item))
}
.size({ width: '100%', height: '100%' })
.padding(15)
.onScrollIndex((start: number, end: number) => {
//在这里必须要调用此方法
this.pagingSource.onVisibleAreaChanged(start, end)
})
.onAppear(() => {
this.isListAppeared = true
})
.onDisAppear(() => {
this.isListAppeared = false
})
.scrollBarWidth(15)
} else {
Button('重试').onClick(() => {
this.pagingSource.refreshData()
})
}
}
}.width('100%').height('100%')
}
}
上图示例参考 entry
更多关于HarmonyOS 鸿蒙Next下实现分页预加载功能的实战教程也可以访问 https://www.itying.com/category-93-b0.html
3 回复
在HarmonyOS Next中实现分页预加载,可使用LazyForEach
组件配合DataPanel
。步骤:
- 定义数据源并实现
IDataSource
接口 - 创建
LazyForEach
组件,设置预加载参数preloadCount
- 在
aboutToAppear
或滚动事件中触发数据预加载 - 使用
@ObjectLink
管理数据状态
关键代码示例:
@Entry
@Component
struct PageList {
private data: MyDataSource = new MyDataSource()
build() {
List() {
LazyForEach(this.data, (item) => {
ListItem() {
Text(item.toString())
}
}, (item) => item.id.toString())
}
}
}
预加载数量通过preloadCount
属性控制。
这是一个关于在HarmonyOS Next中使用ExperimentalPaging库实现分页预加载功能的详细示例。从代码来看,该库参考了Android Paging3的设计,提供了以下核心功能:
- 自动分页加载:当用户滚动到列表末尾时自动加载下一页数据
- 参数跟踪:自动管理前后页的加载参数
- 状态管理:支持加载状态跟踪和错误重试
- 多种布局支持:兼容List、Grid和WaterFlow布局
关键实现要点包括:
- 继承PagingSource类并实现load()方法处理数据加载逻辑
- 对于LazyForEach需要额外实现IDataSource接口
- 必须调用onVisibleAreaChanged()方法触发预加载
- 支持通过refreshData()方法刷新数据
这个实现方案比较完整,既支持传统的LazyForEach方式,也兼容新的Repeat语法,可以作为HarmonyOS分页加载的参考实现。