鸿蒙开发:一个轻盈的上拉下拉刷新组件

前言

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/936985.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ByteCTF2024

wp参考&#xff1a; 2024 ByteCTF wp 2024 ByteCTF WP- Nepnep ByteCTF 2024 writeup by Arr3stY0u 五冠王&#xff01;ByteCTF 2024 初赛WriteUp By W&M ByteCTF 2024 By W&M - W&M Team ByteCTF Re WP - 吾爱破解 - 52pojie.cn 2024 ByteCTF - BediveRe_R…

Node.js教程入门第一课:环境安装

对于一个程序员来说&#xff0c;每学习一个新东西的时候&#xff0c;第一步基本上都是先进行环境的搭建&#xff01; 从本章节开始让我们开始探索Node.js的世界吧! 什么是Node.js? 那么什么是Node.js呢&#xff1f;简单的说Node.js 就是运行在服务端的 JavaScript JavaScript…

弧形导轨的变形因素有哪些?

随着弧形导轨的应用日渐普遍&#xff0c;在日常使用中总会遇到很多各种各样的问题&#xff0c;其中变形是最常见的问题&#xff0c;但通过采取正确的预防和解决措施&#xff0c;可以避免其对设备性能和精度造成的影响&#xff0c;以下是一些主要的变形原因&#xff1a; 1、负载…

SSL证书部署(linux-nginx)

一、SSL证书的作用 HTTP协议无法加密数据,数据传输可能产生泄露、篡改或钓鱼攻击等问题。 SSL证书部署到Web服务器后,可帮助Web服务器和网站间建立可信的HTTPS协议加密链接,为网站安全加锁,保证数据安全传输。 二、前提条件 1.已通过数字证书管理服务控制台签发证书。 …

MATLAB引用矩阵元素的几种方法

引用矩阵元素可以通过索引&#xff0c;也可以通过逻辑值 索引 通过引用元素在矩阵中的位置来提取元素&#xff0c;例如&#xff1a; - 逻辑值 通过某种逻辑运算来使得要提取的值变为逻辑 1 1 1&#xff0c;用 A ( ) A() A()提取即可&#xff0c; A A A为原矩阵的名称。 例如&…

一些浅显易懂的IP小定义

IP归属地&#xff08;也叫IP地址&#xff0c;IP属地&#xff09; 互联网协议地址&#xff0c;每个设备上的唯一的网络身份证明。用于确保网络数据能够精准传送到你的设备上。 基于IP数据云全球IP归属地解析&#xff0c;示例Python代码 curl -X POST https://route.showapi.co…

Jupyter Notebook 切换虚拟环境

方法 切换到需要添加到Jupyter Notebook中的虚拟环境&#xff0c;执行&#xff1a; python -m ipykernel install --name Jupyter Notebook中显示的虚拟环境名称如果遇到 [Errno 13] Permission denied: /usr/local/share/jupyter类似的权限问题&#xff0c;可能是没有对应的…

Blue Ocean 在Jenkins上创建Pipeline使用详解

BlueOcean是Jenkins的一个插件,它提供了一套可视化操作界面来帮助用户创建、编辑Pipeline任务。以下是对BlueOcean中Pipeline操作的详细解释: 一、安装与启动BlueOcean 安装:在Jenkins的“系统管理”->“插件管理”->“可选插件”中搜索“BlueOcean”,然后点击“Ins…

厦门凯酷全科技有限公司正规吗靠谱吗?

随着短视频和直播电商的迅猛发展&#xff0c;越来越多的企业开始将目光投向抖音这一平台。作为国内领先的短视频社交平台&#xff0c;抖音凭借其庞大的用户基础和强大的算法推荐系统&#xff0c;成为众多品牌拓展市场、提升销售的重要渠道。厦门凯酷全科技有限公司&#xff08;…

【安卓开发】【Android Studio】启动时报错“Unable to access Android SDK add-on list”

一、问题描述 在启动Android Studio时&#xff0c;软件报错&#xff1a;Unable to access Android SDK add-on list&#xff0c;报错截图如下&#xff1a; 二、原因及解决方法 初步推测是由于网络节点延迟&#xff0c;无法接入谷歌导致的。点击Cancel取消即可。

KALI安装操作及过程

以下是在计算机上安装 Kali Linux 的详细教程&#xff1a;&#xff08;通常我直接使用虚拟机&#xff09; 解压虚拟机安装包&#xff0c;直接在虚拟机中打开KALI &#xff08;将内存改为4GB&#xff09; 初始密码账号&#xff1a;kali 一、准备工作 下载 Kali Linux 镜像文件…

磁盘空间占用分析工具-wiztree【推荐】

磁盘空间占用分析工具-wiztree【推荐】 如果你遇到过磁盘空间占满、找大文件困难、线上服务器空间飙升等一系列磁盘的问题&#xff0c;并且需要分析文件夹占用空间&#xff0c;传统的方法就是一个一个去看&#xff0c;属实太费劲&#xff0c;效率太低。 而“WizTree”便可解决…

2024年12月11日Github流行趋势

项目名称&#xff1a;maigret 项目维护者&#xff1a;soxoj, kustermariocoding, dependabot, fen0s, cyb3rk0tik项目介绍&#xff1a;通过用户名从数千个站点收集个人档案信息的工具。项目star数&#xff1a;12,055项目fork数&#xff1a;870 项目名称&#xff1a;uv 项目维护…

智能家居WTR096-16S录放音芯片方案,实现语音播报提示及录音留言功能

前言&#xff1a; 在当今社会的高速运转之下&#xff0c;夜幕低垂之时&#xff0c;许多辛勤工作的父母尚未归家。对于肩负家庭责任的他们而言&#xff0c;确保孩童按时用餐与居家安全成为心头大事。此时&#xff0c;家居留言录音提示功能应运而生&#xff0c;恰似家中的一位无形…

基于IEEE 802.1Qci的时间敏感网络(TSN)主干架构安全分析及异常检测系统设计

中文标题&#xff1a;基于IEEE 802.1Qci的时间敏感网络&#xff08;TSN&#xff09;主干架构安全分析及异常检测系统设计 英文标题&#xff1a;Security Analysis of the TSN Backbone Architecture and Anomaly Detection System Design Based on IEEE 802.1Qci 作者信息&…

【专题】2024年中国新能源汽车用车研究报告汇总PDF洞察(附原数据表)

原文链接&#xff1a; https://tecdat.cn/?p38564 本年度&#xff0c;国家及地方政府持续发力&#xff0c;推出诸多政策组合拳&#xff0c;全力推动汽车产业向更高质量转型升级&#xff0c;积极鼓励消费升级&#xff0c;并大力推行以旧换新等惠民生、促发展举措。尤为引人注目…

ESP32-C3 入门笔记07: ESP-NOW动态绑定MAC地址. (ESP-IDF + VSCode)

ESP-NOW 简介 ESP-NOW [gitbuh] ESP-NOW 是一种由乐鑫公司定义的无连接 Wi-Fi 通信协议。在 ESP-NOW 中&#xff0c;应用程序数据被封装在各个供应商的动作帧中&#xff0c;然后在无连接的情况下&#xff0c;从一个 Wi-Fi 设备传输到另一个 Wi-Fi 设备。 CTR 与 CBC-MAC 协…

Elasticsearch02-安装7.x

零、文章目录 Elasticsearch02-安装7.x 1、Windows安装Elasticsearch &#xff08;1&#xff09;JDK安装 Elasticsearch是基于java开发的&#xff0c;所以需要安装JDK。我们安装的Elasticsearch版本是7.15&#xff0c;对应JDK至少1.8版本以上。也可以不安装jdk&#xff0c;…

微信小程序中 crypto-js 加解密全攻略

一、引言 在微信小程序开发中&#xff0c;数据的安全至关重要。加解密技术在保护用户数据和应用程序的安全性方面起着关键作用。小程序在与服务器进行数据交互时&#xff0c;面临着数据泄露、篡改等安全风险。为了确保用户信息的安全&#xff0c;选择合适的加解密算法变得尤为…

fiddler设置抓取https,还抓取不到https如何解决?

一、清楚 C:\Users\Admin\AppData\Roaming\Microsoft\Crypto\RSA 目录下所有文件&#xff08;首次安装fiddler请忽略&#xff09; 二、清除电脑上的根证书&#xff0c;WINR快捷键&#xff0c;输入&#xff1a;certmgr.msc&#xff0c; 然后回车&#xff0c;查找所有fiddler证书…