前言
老早之前开源了一个刷新组件,提供了很多常见的功能,也封装了List,Grid,WaterFlow,虽然功能多,但也冗余比较多,随着时间的前去,暴露的问题就慢慢增多,虽然我也提供了通用的RefrshLayout,奈何很多人仍然有许多问题,但大部分都是相关属性以及用法的问题,对于我来说也比较苦恼,既然如此,那就只封装一个刷新加载,其它的自己实现好了,于是针对refresh的轻盈组件就剥离出来了。
因为它只是一个刷新组件,也仅仅是提供刷新能力,并不提供数据加载服务,这是和refrsh组件的不同之处,当然了,也是灵活之处,毕竟列表的组件是自己写的,需要什么样式更加灵活,但是在代码层次上也稍显冗余,不过有舍就有得。
目前已上传至了中心仓库,地址是:
https://ohpm.openharmony.cn/#/cn/detail/@abner%2Flithe_refresh
刷新库功能效果一览
1、所有功能
2、各个功能效果
快速使用
方式一:在Terminal窗口中,执行如下命令安装三方包,DevEco Studio会自动在工程的oh-package.json5中自动添加三方包依赖。
建议:在使用的模块路径下进行执行命令。
ohpm install @abner/lithe_refresh
方式二:在工程的oh-package.json5中设置三方包依赖,配置示例如下:
"dependencies": { "@abner/lithe_refresh": "^1.0.0"}
使用注意
可以使用LitheRefresh组件,包裹想刷新的任意组件,相对比较灵活,如果您想实现懒加载数据模式,建议结合提供的RefreshDataSource,可以让您实现更加方便。
有一点需要知道,如果是包裹的是可滑动组件,比如List,Grid,WaterFlow等,需要配合nestedScroll属性,来解决滑动之间的冲突。
代码案例
1、简单使用
controller: RefreshController = new RefreshController() //任意组件,可以是List、Grid,WaterFlow …… @Builder itemLayout() { Column() { }.width("100%") .height("100%") .backgroundColor(Color.Pink) .justifyContent(FlexAlign.Center) } LitheRefresh({ itemLayout: this.itemLayout, controller: this.controller, onRefresh: () => { //下拉刷新 this.controller.finishRefresh() }, onLoadMore: () => { //加载更多 this.controller.finishLoadMore() } })
2、List使用
@Entry @Component struct ListUpAndDownPage { @State testArray: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] scroller: Scroller = new Scroller() controller: RefreshController = new RefreshController() @Builder itemLayout(_this: ListUpAndDownPage) { List({ scroller: _this.scroller, space: 20 }) { ForEach(_this.testArray, (item: number) => { ListItem() { Text('' + item) .width('100%') .height(80) .fontSize(16) .textAlign(TextAlign.Center) .backgroundColor(0xFFFFFF) .border({ width: 2, color: Color.Pink }) } }, (item: string, index: number) => JSON.stringify(item) + "_" + index) } .scrollBar(BarState.Off) .edgeEffect(EdgeEffect.None) .width("100%") .height("100%") .padding({ left: 20, right: 20 }) .nestedScroll({ scrollForward: NestedScrollMode.PARENT_FIRST, scrollBackward: NestedScrollMode.PARENT_FIRST }) } build() { Column() { LitheRefresh({ scroller: this.scroller, controller: this.controller, itemLayout: () => { this.itemLayout(this) }, onRefresh: () => { //下拉刷新 setTimeout(() => { this.controller.finishRefresh() }, 2000) }, onLoadMore: () => { //上拉加载 setTimeout(() => { this.testArray.push(13) this.testArray.push(14) this.controller.finishLoadMore() }, 2000) } }) } } }
3、Grid使用
@Entry @Component struct GridUpAndDownPage { @State testArray: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] scroller: Scroller = new Scroller() controller: RefreshController = new RefreshController() @Builder itemLayout(_this: GridUpAndDownPage) { Grid(_this.scroller) { ForEach(_this.testArray, (item: number) => { GridItem() { Text('' + item) .width('100%') .height(80) .fontSize(16) .textAlign(TextAlign.Center) .backgroundColor(0xFFFFFF) .border({ width: 2, color: Color.Pink }) } }, (item: string, index: number) => JSON.stringify(item) + "_" + index) } .columnsTemplate("1fr 1fr") .columnsGap(10) .rowsGap(10) .scrollBar(BarState.Off) .edgeEffect(EdgeEffect.None) .width("100%") .height("100%") .padding({ left: 20, right: 20 }) .nestedScroll({ scrollForward: NestedScrollMode.PARENT_FIRST, scrollBackward: NestedScrollMode.PARENT_FIRST }) } build() { Column() { ActionBar({ title: "Grid组件刷新", barBackgroundColor: Color.Red, leftText: "返回", onLeftClick: () => { router.back() }, }) LitheRefresh({ scroller: this.scroller, controller: this.controller, itemLayout: () => { this.itemLayout(this) }, onRefresh: () => { //下拉刷新 setTimeout(() => { this.controller.finishRefresh() }, 2000) }, onLoadMore: () => { //上拉加载 setTimeout(() => { this.testArray.push(13) this.testArray.push(14) this.controller.finishLoadMore() }, 2000) } }) } } }
3、WaterFlow使用
这里没有使用提供RefreshDataSource,所以懒加载方式比较冗余,为了简洁代码,建议使用我提供的RefreshDataSource,可以让您的效率极大提升。
具体RefreshDataSource使用方式,可以查看Demo中LazyDataOperationPage页面。
@Entry @Component struct WaterFlowUpAndDownPage { scroller: Scroller = new Scroller() controller: RefreshController = new RefreshController() @Builder itemLayout(_this: WaterFlowUpAndDownPage) { WaterFlowView({ scroller: _this.scroller }) } build() { Column() { LitheRefresh({ scroller: this.scroller, controller: this.controller, itemLayout: () => { this.itemLayout(this) }, onRefresh: () => { //下拉刷新 setTimeout(() => { this.controller.finishRefresh() }, 2000) }, onLoadMore: () => { //上拉加载 setTimeout(() => { this.controller.finishLoadMore() }, 2000) } }) } } } @Component struct WaterFlowView { @State minSize: number = 80 @State maxSize: number = 180 @State fontSize: number = 24 @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F] scroller: Scroller = new Scroller() dataSource: WaterFlowDataSource = new WaterFlowDataSource() private itemWidthArray: number[] = [] private itemHeightArray: number[] = [] // 计算FlowItem宽/高 getSize() { let ret = Math.floor(Math.random() * this.maxSize) return (ret > this.minSize ? ret : this.minSize) } // 设置FlowItem的宽/高数组 setItemSizeArray() { for (let i = 0; i < 30; i++) { this.itemWidthArray.push(this.getSize()) this.itemHeightArray.push(this.getSize()) } } aboutToAppear() { this.setItemSizeArray() } build() { WaterFlow({ scroller: this.scroller }) { LazyForEach(this.dataSource, (item: number) => { FlowItem() { Column() { Text("N" + item).fontSize(12).height('16') } } .width('100%') .height(this.itemHeightArray[item % 30]) .backgroundColor(this.colors[item % 5]) }, (item: string) => item) } .columnsTemplate("1fr 1fr") .columnsGap(10) .rowsGap(5) .backgroundColor(0xFAEEE0) .width('100%') .height('100%') .nestedScroll({ scrollForward: NestedScrollMode.PARENT_FIRST, scrollBackward: NestedScrollMode.PARENT_FIRST }) } } // 实现IDataSource接口的对象,用于瀑布流组件加载数据 export class WaterFlowDataSource implements IDataSource { private dataArray: number[] = [] private listeners: DataChangeListener[] = [] constructor() { for (let i = 0; i < 30; i++) { this.dataArray.push(i) } } // 获取索引对应的数据 public getData(index: number): number { return this.dataArray[index] } // 通知控制器数据重新加载 notifyDataReload(): void { this.listeners.forEach(listener => { listener.onDataReloaded() }) } // 通知控制器数据增加 notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index) }) } // 通知控制器数据变化 notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index) }) } // 通知控制器数据删除 notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index) }) } // 通知控制器数据位置变化 notifyDataMove(from: number, to: number): void { this.listeners.forEach(listener => { listener.onDataMove(from, to) }) } //通知控制器数据批量修改 notifyDatasetChange(operations: DataOperation[]): void { this.listeners.forEach(listener => { listener.onDatasetChange(operations); }) } // 获取数据总数 public totalCount(): number { return this.dataArray.length } // 注册改变数据的控制器 registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { this.listeners.push(listener) } } // 注销改变数据的控制器 unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener) if (pos >= 0) { this.listeners.splice(pos, 1) } } // 增加数据 public add1stItem(): void { this.dataArray.splice(0, 0, this.dataArray.length) this.notifyDataAdd(0) } // 在数据尾部增加一个元素 public addLastItem(): void { this.dataArray.splice(this.dataArray.length, 0, this.dataArray.length) this.notifyDataAdd(this.dataArray.length - 1) } // 在指定索引位置增加一个元素 public addItem(index: number): void { this.dataArray.splice(index, 0, this.dataArray.length) this.notifyDataAdd(index) } // 删除第一个元素 public delete1stItem(): void { this.dataArray.splice(0, 1) this.notifyDataDelete(0) } // 删除第二个元素 public delete2ndItem(): void { this.dataArray.splice(1, 1) this.notifyDataDelete(1) } // 删除最后一个元素 public deleteLastItem(): void { this.dataArray.splice(-1, 1) this.notifyDataDelete(this.dataArray.length) } // 在指定索引位置删除一个元素 public deleteItem(index: number): void { this.dataArray.splice(index, 1) this.notifyDataDelete(index) } // 重新加载数据 public reload(): void { this.dataArray.splice(1, 1) this.dataArray.splice(3, 2) this.notifyDataReload() } }
使用总结
在和可滑动组件使用的时候,记得一定要和nestedScroll属性配合使用,用于解决滑动冲突,除此之外,还需要传递滑动组件的scroller属性,用于手势操作。