前言
老早之前开源了一个刷新组件,提供了很多常见的功能,也封装了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属性,用于手势操作。