Skip to content

iHongRen/RefreshList

Repository files navigation

RefreshList

License HarmonyOS Version

鸿蒙HarmonyOS NEXT 简单易用的上拉下拉刷新组件,支持自定义样式和多种使用场景。

如果项目对你有帮助,欢迎持续关注和 🌟Star💖赞助

✨ 特性

  • 支持下拉刷新和上拉加载更多

  • 支持自定义刷新头部和底部组件

  • 支持自定义加载中和空页面组件

  • 支持自定义 HeaderView 组件

  • 支持分组列表

  • 支持全局自定义各种组件

  • 完善的 demo 示例

📦 安装

通过 ohpm 安装

ohpm install @cxy/refreshlist

通过依赖安装

在项目的 oh-package.json5 文件中添加依赖, 然后执行同步操作:

{
  "dependencies": {
    "@cxy/refreshlist": "^1.0.0"
  }
}
demo.jpeg
Demo 页面
simple.gif
简单示例
group.gif
分组示例
headerview.gif
自定义HeaderView示例
custom.gif
各种自定义示例
chat.gif
聊天示例
dynamic.gif
动态刷新示例
infinite.gif
无限加载示例
search.gif
搜索示例
grid.gif
网格示例

advanced.gif
高级使用示例
global.gif
全局配置示例

🚀 快速开始

import { RefreshController, RefreshDataSource, RefreshList } from "@cxy/refreshlist"

// 1️⃣ 创建数据模型
class ItemModel {
  id: string = ''
  title: string = ''

  constructor(id: string = '', title: string = '') {
    this.id = id
    this.title = title
  }
}

// 2️⃣ 创建ViewModel
class SimpleViewModel {
  @Track dataSource: RefreshDataSource = new RefreshDataSource()
  @Track controller: RefreshController = new RefreshController()
  private currentPage: number = 1
  private pageSize: number = 20

  refresh(): void {
    this.requestData(1)
  }

  loadMore(): void {
    this.requestData(this.currentPage + 1)
  }

  private async requestData(page: number): Promise<void> {
    // 模拟网络请求延迟
    setTimeout(() => {
      this.currentPage = page
      const data = this.generateSimpleData(this.pageSize)
      if (page === 1) {
        this.dataSource.deleteAll()
      }
      this.dataSource.pushDataArray(data)

      // 模拟最多5页数据
      const hasMore = page < 5
      this.controller.setHasmore(hasMore)
      this.controller.finishRefresh()
    }, 500)
  }

  private generateSimpleData(count: number): ItemModel[] {
    const result: ItemModel[] = []
    for (let i = 0; i < count; i++) {
      const globalIndex = (this.currentPage - 1) * this.pageSize + i
      const item = new ItemModel(`simple_${globalIndex}`, `Title - ${globalIndex + 1}`)
      result.push(item)
    }
    return result
  }
}


// 3️⃣ 使用组件
@Entry
@Component
struct Index {
  @State viewModel: SimpleViewModel = new SimpleViewModel()

  aboutToAppear() {
    this.viewModel.refresh()
  }

  build() {
    Column() {
      RefreshList({
        dataSource: this.viewModel.dataSource,
        controller: this.viewModel.controller,
        onRefresh: () => this.viewModel.refresh(),
        onLoadMore: () => this.viewModel.loadMore(),
        itemLayout: (item: Object, index: number) => this.itemLayout(item as ItemModel),
        divider: { strokeWidth: 0.5, color: '#f0f0f0' },
        keyGenerator: (item: ItemModel) => item.id
      })
    }
  }

  @Builder
  itemLayout(item: ItemModel): void {
    ListItem() {
      Row() {
        Text(item.title)
          .fontSize(16)
          .fontColor('#333')
          .fontWeight(FontWeight.Medium)
          .layoutWeight(1)

        Image($r('sys.media.ohos_ic_public_arrow_right'))
          .width(16)
          .height(16)
          .fillColor('#ccc')
      }
      .width('100%')
      .padding(16)
      .backgroundColor('#fff')
    }
    .onClick(() => {
      console.log(`点击项目: ${item.title}`)
    })
  }
}

🎉 就是这么简单! 三步即可拥有一个功能完整的刷新列表。

📚 API 文档

RefreshList 组件属性

必需属性

属性 类型 说明
dataSource RefreshDataSource | RefreshGroupDataSource 数据源,管理列表数据
controller RefreshController 控制器,用于控制刷新状态和列表操作

布局属性

属性 类型 默认值 说明
itemLayout (item: Object, index: number) => void - 列表项布局
customLayout () => void - 自定义布局,完全自定义LazyForEach部分
headerLayout () => void - 列表头部布局,类似iOS的tableHeaderView
loadingLayout () => void 默认加载视图 加载中状态的布局
emptyLayout () => void 默认空视图 空数据状态的布局
refreshHeaderLayout () => void 默认刷新头部 自定义下拉刷新头部布局
refreshFooterLayout () => void 默认刷新底部 自定义上拉加载底部布局

数据状态属性

属性 类型 默认值 说明
refreshHeaderData RefreshHeaderData - 下拉刷新头部数据,用于自定义刷新头部状态
refreshFooterData RefreshFooterData - 上拉加载底部数据,用于自定义加载底部状态
showLoading boolean true 是否显示加载状态
showEmpty boolean true 是否显示空数据状态

列表配置属性

属性 类型 默认值 说明
cachedCount number 4 缓存的列表项数量,用于性能优化
showLoadMoreGreaterCount number 5 当item大于多少时,才显示加载更多组件,通常为一屏能显示的item数量
contentStartOffset number - 设置内容区域起始偏移量
contentEndOffset number - 设置内容区末尾偏移量
sticky StickyStyle StickyStyle.Header | Footer 吸顶样式
itemSpace number - 列表项间距
barState BarState BarState.On 滚动条状态
scrollBarColor Color | number | string - 滚动条颜色
nestedScroll NestedScrollOptions - 设置前后两个方向的嵌套滚动模式,实现与父组件的滚动联动
enableScrollInteraction boolean - 设置是否支持滚动手势
pullDownRatio number - 设置下拉跟手系数,禁止下拉设置0
divider RefreshListDivider null 分割线样式
lanes number - 设置List组件的布局列数或行数(网格布局)
gutter Dimension - 列间距(网格布局时使用)
maintainVisibleContentPosition boolean false 插入或删除数据时是否保持可见内容位置不变
backToTop boolean true 设置滚动组件是否支持点击状态栏回到顶部(API version 15+)
edfeEffect EdgeEffect - List的EdgeEffect效果
listAttrModifier RefreshListAttrModifier - 用于自定义更多List属性

回调函数

属性 类型 说明
onRefresh () => void 下拉刷新时的回调函数
onLoadMore () => void 上拉加载更多时的回调函数
keyGenerator (item: ESObject, index: number) => string 列表项唯一标识生成器
onDidScroll OnScrollCallback 滚动时的回调函数
onReachEnd () => void 滚动到底部时的回调函数
onScrollIndex (start: number, end: number) => void 滚动到索引时的回调函数,可用于实现无感知预加载

滚动控制器

属性 类型 说明
scroller ListScroller 列表滚动控制器,可用于获取滚动位置等信息和控制滚动

RefreshController 控制器

RefreshController 提供了控制刷新列表的各种方法:

属性

属性 类型 说明
scroller ListScroller 列表滚动控制器,可用于获取滚动位置等信息和控制滚动

方法

方法 参数 返回值 说明
finishRefresh () void 结束刷新状态,必须在刷新完成后调用
setHasmore (hasmore: boolean) void 设置是否还有更多数据可加载
hideLoadMore (hide: boolean) void 隐藏或显示加载更多组件
onRefresh () void 手动触发下拉刷新
scrollToIndex (index: number, smooth?: boolean, align?: ScrollAlign, options?: ScrollToIndexOptions) void 滚动到指定索引位置

内部回调属性(由组件自动设置)

属性 类型 说明
setHasmore (hasmore: boolean) => void 设置是否还有更多数据
onRefresh () => void 刷新回调
finishRefresh () => void 完成刷新回调
hideLoadMore (hide: boolean) => void 隐藏加载更多回调
scrollToIndex (value: number, smooth?: boolean, align?: ScrollAlign, options?: ScrollToIndexOptions) => void 滚动到指定索引回调

使用示例

export class SimpleViewModel {
  controller: RefreshController = new RefreshController()

  refresh(): void {
    // 模拟网络请求
    setTimeout(() => {
      // 处理数据...
      
      // 设置是否还有更多数据
      this.controller.setHasmore(hasMore)
      
      // 必须调用finishRefresh结束刷新状态
      this.controller.finishRefresh()
     
    }, 1000)
  }

  // 手动触发刷新
  manualRefresh(): void {
    this.controller.onRefresh()
  }

  // 滚动到顶部
  scrollToTop(): void {
    this.controller.scrollToIndex(0, true)
  }

  // 获取当前滚动位置
  getCurrentOffset(): number {
    return this.controller.scroller.currentOffset().yOffset
  }
}

RefreshDataSource 数据源

RefreshDataSource 是管理列表数据的核心类,实现了 IDataSource 接口:

基础方法

方法 参数 返回值 说明
isEmpty () boolean 判断数据源是否为空
totalCount () number 获取数据总数 (分组时,为分组数量)
totalItemCount () number 获取数据项总数(分组时,为分组下item总数)
getData (index: number) Object | undefined 获取指定索引的数据
getLastData () Object | undefined 获取最后一项数据
getDataAll () Object[] 获取所有数据的副本

数据操作方法

方法 参数 返回值 说明
insertData (index: number, data: Object) void 在指定位置插入单个数据
insertDataArray (index: number, arr: Object[]) void 在指定位置插入数据数组
pushData (data: Object) void 在末尾添加单个数据
pushDataArray (arr: Object[]) void 在末尾添加数据数组
deleteIndex (index: number) void 删除指定索引的数据
deleteData (data: Object) void 删除指定数据对象
deleteAll () void 删除所有数据
deleteIndexCount (index: number, count: number) void 从指定索引开始删除指定数量的数据
repalceIndex (index: number, data: Object, key?: string) void 替换指定索引的数据
reloadIndex (index: number, key?: string) void 重新加载指定索引的数据
reloadData (data: Object, key?: string) void 重新加载指定数据对象
reloadDataAll () void 重新加载所有数据
moveDataIndex (from: number, to: number) void 移动数据从一个位置到另一个位置

监听器方法

方法 参数 返回值 说明
registerDataChangeListener (listener: DataChangeListener) void 注册数据变化监听器
unregisterDataChangeListener (listener: DataChangeListener) void 取消注册数据变化监听器

回调属性

属性 类型 说明
onDataCountChange (count: number) => void 数据数量变化时的回调

RefreshGroupDataSource 分组数据源

RefreshGroupDataSource 继承自 RefreshDataSource,专门用于管理分组列表数据:

重写方法

方法 参数 返回值 说明
isEmpty () boolean 判断分组数据源是否为空
totalItemCount () number 计算所有分组中的数据项总数
getGroupDataAll () Object[] 获取所有分组中的数据项

分组特有方法

方法 参数 返回值 说明
addListToGroup (list: Object[], getTitle: (e: Object) => string) void 将数据列表按标题分组添加

使用示例

// 基础数据源使用
export class SimpleViewModel {
  dataSource: RefreshDataSource = new RefreshDataSource()

  refresh(): void {
    // 清空现有数据
    this.dataSource.deleteAll()
    
    // 添加新数据
    const newData = this.generateData()
    this.dataSource.pushDataArray(newData)
  }

  addItem(item: ItemModel): void {
    this.dataSource.pushData(item)
  }

  removeItem(item: ItemModel): void {
    this.dataSource.deleteData(item)
  }

  updateItem(index: number, newItem: ItemModel): void {
    this.dataSource.repalceIndex(index, newItem)
  }
}

// 分组数据源使用
export class GroupViewModel {
  dataSource: RefreshGroupDataSource = new RefreshGroupDataSource()

  refresh(): void {
    this.dataSource.deleteAll()
    
    const allItems = this.generateData()
    // 按category字段自动分组
    this.dataSource.addListToGroup(allItems, (item) => item.category)
  }
}

RefreshGroupModel 分组模型

分组数据的模型类:

属性

属性 类型 说明
title string 分组标题
dataSource RefreshDataSource 分组内的数据源
data Object 可选的附加数据

构造函数

constructor(title: string, dataSource: RefreshDataSource)

RefreshHeaderData 刷新头部数据

下拉刷新头部的状态数据:

属性 类型 默认值 说明
state RefreshStatus RefreshStatus.Inactive 刷新状态(Inactive、Drag、OverDrag、Refresh、Done)
offset number 0 下拉偏移量
dragText ResourceStr '下拉刷新' 下拉时显示的文本
overDragText ResourceStr '释放刷新' 超过阈值时显示的文本
refreshText ResourceStr '刷新中...' 刷新中显示的文本
doneText ResourceStr '刷新完成' 刷新完成显示的文本
textColor ResourceColor '#bbb' 文本颜色
font Font { size: 13 } 文本字体
loadingColor ResourceColor | LinearGradient LinearGradient loading 颜色
loadingSize SizeOptions { width: 20, height: 20 } loading 大小

方法

方法 参数 返回值 说明
getText () ResourceStr 根据当前状态返回对应的文本

RefreshFooterData 刷新底部数据

上拉加载更多底部的状态数据:

属性 类型 默认值 说明
isShow boolean true 是否显示底部组件
state RefreshFooterState RefreshFooterState.None 加载状态
noneText ResourceStr '上拉加载更多' 默认状态显示的文本
loadingText ResourceStr '加载中...' 加载中显示的文本
noMoreText ResourceStr '没有更多了' 没有更多数据时显示的文本
textColor ResourceColor '#bbb' 文本颜色
font Font { size: 13 } 文本字体
loadingColor ResourceColor '#bbb' loading 颜色
loadingSize SizeOptions { width: 20, height: 20 } loading 大小

方法

方法 参数 返回值 说明
getText () ResourceStr 根据当前状态返回对应的文本

RefreshFooterState 枚举

加载更多的状态枚举:

数值 说明
None 0 默认状态
Loading 1 正在加载中
NoMore 2 没有更多数据
NoMore 2 没有更多数据

RefreshGlobalConfig 全局配置

全局配置类,用于设置所有RefreshList组件的默认样式和行为:

静态属性

属性 类型 默认值 说明
headerBuilder WrappedBuilder<[IRefreshHeaderData]> - 全局自定义刷新头部构建器
footerBuilder WrappedBuilder<[IRefreshFooterData]> - 全局自定义刷新底部构建器
loadingBuilder WrappedBuilder<[]> - 全局自定义加载构建器
emptyBuilder WrappedBuilder<[]> - 全局自定义空状态构建器
headerInactiveText ResourceStr '刷新' 头部非活动状态文本
headerDragText ResourceStr '下拉刷新' 头部下拉状态文本
headerOverDragText ResourceStr '释放刷新' 头部超过阈值状态文本
headerRefreshText ResourceStr '刷新中...' 头部刷新中状态文本
headerDoneText ResourceStr '刷新完成' 头部刷新完成状态文本
footerNoneText ResourceStr '上拉加载更多' 底部默认状态文本
footerLoadingText ResourceStr '加载中...' 底部加载中状态文本
footerNoMoreText ResourceStr '没有更多了' 底部没有更多数据状态文本
headerTextColor ResourceColor '#bbb' 头部文本颜色
footerTextColor ResourceColor '#bbb' 底部文本颜色
headerTextFont Font { size: 13 } 头部文本字体
footerTextFont Font { size: 13 } 底部文本字体
headerLoadingColor ResourceColor | LinearGradient LinearGradient 头部loading颜色
footerLoadingColor ResourceColor '#bbb' 底部loading颜色
headerLoadingSize SizeOptions { width: 20, height: 20 } 头部loading大小
footerLoadingSize SizeOptions { width: 20, height: 20 } 底部loading大小

使用示例

import { RefreshGlobalConfig, IRefreshHeaderData, RefreshStatus } from '@cxy/refreshlist'

// 全局自定义刷新头部
@Builder
function globalHeaderBuilder(item: IRefreshHeaderData) {
  GlobalHeader({ data: item.data })
}

// 配置全局设置
function setupGlobalConfig() {
  RefreshGlobalConfig.headerBuilder = wrapBuilder(globalHeaderBuilder)
  RefreshGlobalConfig.headerDragText = '下拉刷新数据'
  RefreshGlobalConfig.headerOverDragText = '释放立即刷新'
  RefreshGlobalConfig.headerRefreshText = '正在刷新数据...'
  RefreshGlobalConfig.headerDoneText = '刷新成功'
  RefreshGlobalConfig.footerNoneText = '上拉加载更多'
  RefreshGlobalConfig.footerLoadingText = '数据加载中...'
  RefreshGlobalConfig.footerNoMoreText = '亲,没有更多了'
  RefreshGlobalConfig.headerTextColor = '#333'
  RefreshGlobalConfig.footerTextColor = '#666'
}

// 在应用启动时调用
setupGlobalConfig()

IRefreshHeaderData 接口

刷新头部数据接口:

属性 类型 说明
data RefreshHeaderData 刷新头部状态数据

IRefreshFooterData 接口

刷新底部数据接口:

属性 类型 说明
data RefreshFooterData 刷新底部状态数据

RefreshListDivider 分割线接口

自定义分割线样式的接口:

属性 类型 必需 说明
strokeWidth Length 分割线宽度
color ResourceColor 分割线颜色
startMargin Length 起始边距
endMargin Length 结束边距

RefreshListAttrModifier 属性修饰器

用于自定义更多List属性的修饰器类:

export class RefreshListAttrModifier implements AttributeModifier<ListAttribute> {
  applyNormalAttribute(instance: ListAttribute): void {
    // 在这里可以自定义更多List属性
    // 例如:背景色、边框、阴影等
  }
}

// 使用示例
class CustomAttrModifier extends RefreshListAttrModifier {
  applyNormalAttribute(instance: ListAttribute): void {
    instance.backgroundColor('#f5f5f5')
    instance.borderRadius(10)
    instance.padding(10)
  }
}

❓常见问题

Q: 如何禁用下拉刷新?

// 方法1:不传onRefresh回调
RefreshList({
  // ... 其他属性
})

// 方法2:设置pullDownRatio为0
RefreshList({
  pullDownRatio: 0,
  // ... 其他属性
})

Q: 如何禁用上拉加载更多?

// 方法1:不传onLoadMore回调(推荐)
RefreshList({
  onRefresh: () => this.refresh(),
  // 不传onLoadMore即可禁用上拉加载
})

// 方法2:动态控制显示/隐藏
this.controller.hideLoadMore(true)  // 隐藏加载更多
this.controller.setHasmore(false)   // 设置没有更多数据

Q: 如何监听列表滚动事件?

RefreshList({
  onDidScroll: (scrollOffset: number, scrollState: ScrollState) => {
    console.log(`滚动偏移: ${scrollOffset}`)
    // 可以根据滚动位置实现悬浮按钮显示/隐藏等功能
  },
  onScrollIndex: (start: number, end: number) => {
    console.log(`当前可见范围: ${start} - ${end}`)
    // 可以用于埋点统计、预加载等
  },
  onReachEnd: () => {
    console.log('滚动到底部')
    // 可以用于触发加载更多数据
  }
})

作者

@仙银 鸿蒙开源作品,欢迎持续关注 🌟Star💖赞助

1、hpack - 鸿蒙内部测试分发,一键脚本打包工具

2、Open-in-DevEco-Studio - macOS 直接在 Finder 工具栏上,使用 DevEco-Studio 打开鸿蒙工程。

3、cxy-theme - DevEco-Studio 绿色背景主题

4、harmony-udid-tool - 简单易用的 HarmonyOS 设备 UDID 获取工具,适用于非开发人员。

5、SandboxFinder - 鸿蒙沙箱文件浏览器

6、WebServer - 鸿蒙轻量级Web服务器框架

7、SelectableMenu - 适用于聊天对话框中的文本选择菜单

8、RefreshList - 功能完善的上拉下拉加载组件,支持各种自定义。

About

鸿蒙HarmonyOS,简单易用的上拉下拉加载组件

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published