HarmonyOS NEXT 阅读翻页方式案例

news2024/11/16 15:47:37

介绍

本示例展示手机阅读时左右翻页,上下翻页,覆盖翻页的功能。

效果图预览

img

使用说明

  1. 进入模块即是左右翻页模式。
  2. 点击屏幕中间区域弹出上下菜单。
  3. 点击设置按钮,弹出翻页方式切换按钮,点击可切换翻页方式。
  4. 左右翻页方式可点击翻页,也可滑动翻页,点击屏幕左边1/3区域向左翻页,点击中间1/3区域弹出菜单,点击屏幕右边1/3区域向右翻页。
  5. 上下翻页方式只可上下滑动翻页。
  6. 覆盖翻页方式可点击翻页,也可滑动翻页,点击屏幕左边1/3区域向左翻页,点击中间1/3区域弹出菜单,点击屏幕右边1/3区域向右翻页。

实现思路

本例涉及的关键特性和实现方案如下:

场景一: 左右翻页方式通过swiper+lazyforeach+cachecount实现按需加载。

实现步骤:

  1. aboutToAppear()方法中通过pushItem向后加载数据,addItem向前加载数据。

  2. 使用Swiper组件和LazyForEach将数据源中的每条数据存放于Text组件中,Swiper向左或向右滑动的效果就是左右翻页的效果。

  3. 需要网络加载时可在BasicDataSourcegetData方法中进行。当index等于0向前申请网络数据,当index等于this.totalCount() - 1时向后请求网络数据。

  4. 请求完数据后可通过push方法将数据插入到队尾,通过unshift插入到队头,具体可参考BasicDataSourcepushItemaddItem方法。

    源码参考:
    LeftRightFlipPage.ets
    BasicDataSource.ets。

 Swiper(this.swiperController) { 
   /**
    * TODO: 高性能知识点: 使用了cachedCount设置预加载的Text的数量,只在LazyForEach中生效,设置该属性后会缓存cachedCount个Text,LazyForEach超出显示和缓存范围的Text会被释放。
    * 使用cachedCount参数的例子:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/ui-ts-performance-improvement-recommendation-0000001477981001-V2
    */
   LazyForEach(this.data, (item: string) => {
      Text($r(item))
         ...
   }, (item: string) => item)
 }
  // TODO:知识点:index设置当前在容器中显示的子组件的索引值。设置小于0或大于等于子组件数量时,按照默认值0处理。
 .index(this.currentPageNum - CONFIGURATION.PAGEFLIPPAGECOUNT)
 .width($r('app.string.pageflip_full_size'))
 .height($r('app.string.pageflip_full_size'))
 .indicator(false)
 .cachedCount(CONFIGURATION.PAGEFLIPCACHECOUNT)
 ...
aboutToAppear(): void {
  /**
   * 请求网络数据之后可以通过this.data.addItem(new Item('app.string.content' + i.toString()));的方法插入到数据源的开头形成新的数据源。
   * 请求网络数据之后可以通过this.data.pushItem(new Item('app.string.content' + i.toString()));的方法插入到数据源的末尾形成新的数据源。
   */
  for (let i = CONFIGURATION.PAGEFLIPPAGESTART; i <= CONFIGURATION.PAGEFLIPPAGEEND; i++) {
    this.data.pushItem(STRINGCONFIGURATION.PAGEFLIPRESOURCE + i.toString());
  }
}
public getData(index: number): string {
  /**
   * TODO:知识点:1.当index等于this.totalCount() - 1时向后请求网络数据。当index等于0时向前请求网络数据。
   * TODO:知识点:2.新请求到的数据可以通过push插入到队尾,通知listeners刷新添加可参考pushItem方法。如果想要插到队头可以通过unshift插入到队头,通知listeners刷新添加可参考addItem方法。
   */
  return this.elements[index];
}
场景二: 上下翻页方式通过list+lazyforeach+cachecount实现按需加载。

实现步骤:

  1. aboutToAppear()方法中通过pushItem向后加载数据,addItem向前加载数据。

  2. 使用List组件和LazyForEach将数据源中的每条数据存放于Text组件中,List向上或向下滑动的效果就是上下翻页的效果。

  3. 需要网络加载时可在BasicDataSourcegetData方法中进行。当index等于0向前申请网络数据,当index等于this.totalCount() - 1时向后请求网络数据。

  4. 请求完数据后可通过push方法将数据插入到队尾,通过unshift插入到队头,具体可参考BasicDataSourcepushItemaddItem方法。

    源码参考:
    UpDownFlipPage.ets
    BasicDataSource.ets。

// TODO:知识点:initialIndex设置为负数或超过了当前List最后一个item的索引值时视为无效取值,无效取值按默认值0显示。
List({ initialIndex: this.currentPageNum - CONFIGURATION.PAGEFLIPPAGECOUNT }) {
   /**
    * TODO: 高性能知识点: 使用了cachedCount设置预加载的ListItem的数量,只在LazyForEach中生效,设置该属性后会缓存cachedCount个ListItem,LazyForEach超出显示和缓存范围的ListItem会被释放。
    * 使用cachedCount参数的例子:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/ui-ts-performance-improvement-recommendation-0000001477981001-V2
    */
   LazyForEach(this.data, (item: string) => {
      ListItem() {
         Text($r(item))
            ...
      }
   }, (item: string) => item)
}
.width($r('app.string.pageflip_bottomview_row_text_width'))
.height($r('app.string.pageflip_full_size'))
.scrollBar(BarState.Off)
.cachedCount(CONFIGURATION.PAGEFLIPCACHECOUNT)
.onScrollIndex((firstIndex: number) => {
 this.currentPageNum = firstIndex + CONFIGURATION.PAGEFLIPPAGECOUNT;  // 通过onScrollIndex监听当前处于第几页。
})
aboutToAppear(): void {
  /**
   * 请求网络数据之后可以通过this.data.addItem(new Item('app.string.content' + i.toString()));的方法插入到数据源的开头形成新的数据源。
   * 请求网络数据之后可以通过this.data.pushItem(new Item('app.string.content' + i.toString()));的方法插入到数据源的末尾形成新的数据源。
   */
  for (let i = CONFIGURATION.PAGEFLIPPAGESTART; i <= CONFIGURATION.PAGEFLIPPAGEEND; i++) {
    this.data.pushItem(STRINGCONFIGURATION.PAGEFLIPRESOURCE + i.toString());
  }
}
public getData(index: number): string {
  /**
   * TODO:知识点:1.当index等于this.totalCount() - 1时向后请求网络数据。当index等于0时向前请求网络数据。
   * TODO:知识点:2.新请求到的数据可以通过push插入到队尾,通知listeners刷新添加可参考pushItem方法。如果想要插到队头可以通过unshift插入到队头,通知listeners刷新添加可参考addItem方法。
   */
  return this.elements[index];
}
场景三: 覆盖翻页方式通过三个Stack组件通过滑动+动画+改变组件内容实现效果。

实现步骤:

  1. Stack组件中布局三个ReaderPagemidPage位于中间可以根据this.offsetX实时translate自己的位置。

  2. 当this.offsetX<0时,translate的x为this.offsetX,midPage向左移动,显现rightPage

  3. 当this.offsetX>0,translate的x为0,midPage不动,leftPage向右滑动。

  4. 将滑动翻页的动画和点击翻页的动画封装在一个闭包中,由isClick来判断是点击翻页还是滑动翻页,由isLeft来判断点击翻页中是向左翻页还是向右翻页。

  5. 确定翻页时将this.offsetX设置为this.screenW或者-this.screenW。translate移动加上动画效果就会产生覆盖翻页的效果。

  6. 最终滑动动画结束时this.offsetX都会被置为0,leftPage和midPage回归原位。

  7. 当动画结束时由于翻页会让this.currentPageNum加一或减一,根据相应的页数来加载三个content相应的内容。

    源码参考:
    CoverFlipPage.ets。

Stack() {
   ReaderPage({ content: this.rightPageContent }); // 当midPage向左滑时,rightPage开始显现。
   ReaderPage({ content: this.midPageContent })
      /** TODO: 知识点:
       * 当this.offsetX<0时,translate的x为this.offsetX,midPage向左移动,显现rightPage。
       * 当this.offsetX>0,translate的x为CONFIGURATION.PAGEFLIPZERO,midPage不动,leftPage向右滑动。
       */
      .translate({
         x: this.offsetX >= CONFIGURATION.PAGEFLIPZERO ? CONFIGURATION.PAGEFLIPZERO : this.offsetX,
         y: CONFIGURATION.PAGEFLIPZERO,
         z: CONFIGURATION.PAGEFLIPZERO
      })
      .width(this.screenW);
   ReaderPage({ content: this.leftPageContent }) 
      // TODO: 知识点:在midPage的左边,当向右滑时,跟随this.offsetX向右滑动。
      .translate({
         x: -this.screenW + this.offsetX
      });
}
private clickAnimateTo(isClick: boolean, isLeft?: boolean) {
   animateTo({
      duration: CONFIGURATION.PAGEFLIPTOASTDURATION,
      curve: Curve.EaseOut,
      onFinish: () => {
         /** TODO: 知识点:this.currentPageNum加一或者减一后修改组件的内容。
          * 右滑:1. 恢复页面原始状态 2. 修改组件的内容为 page1 = content1-1, page2 = content2-1,page3 = content3-1
          * 左滑:1. 恢复页面原始状态 2. 修改组件的内容为 page1 = content1+1, page2 = content2+1,page3 = content3+1
          */
         if (this.offsetX > CONFIGURATION.PAGEFLIPRIGHTFLIPOFFSETX && this.currentPageNum !== CONFIGURATION.PAGEFLIPPAGESTART) {
            this.currentPageNum -= CONFIGURATION.PAGEFLIPPAGECOUNT;
         } else if (this.offsetX < CONFIGURATION.PAGEFLIPLEFTFLIPOFFSETX && this.currentPageNum !== CONFIGURATION.PAGEFLIPPAGEEND) {
            this.currentPageNum += CONFIGURATION.PAGEFLIPPAGECOUNT;
         }
         this.offsetX = CONFIGURATION.PAGEFLIPZERO;
         this.simulatePageContent();
      }
   }, () => {
      if (isClick) { // 是否为点击翻页
         if (isLeft) {
            this.offsetX = this.screenW; // TODO: 知识点:右滑距离变为一个屏幕宽度,ReaderPage就会向右移动一个屏幕宽度,加上动画,形成了覆盖翻页的效果。
         } else {
            this.offsetX = -this.screenW; // TODO: 知识点:左滑距离变为一个屏幕宽度,ReaderPage就会向左移动一个屏幕宽度,加上动画,形成了覆盖翻页的效果。
         }
      } else { // 滑动翻页
         if (this.offsetX > CONFIGURATION.PAGEFLIPRIGHTFLIPOFFSETX && this.currentPageNum !== CONFIGURATION.PAGEFLIPPAGESTART) {
            this.offsetX = this.screenW;
         } else if (this.offsetX < CONFIGURATION.PAGEFLIPLEFTFLIPOFFSETX && this.currentPageNum !== CONFIGURATION.PAGEFLIPPAGEEND) {
            this.offsetX = -this.screenW;
         } else {
            this.offsetX = CONFIGURATION.PAGEFLIPZERO; // 当位于第一页和末尾页,移动距离设为0,无法翻页。
         }
      }
   });
}
// 模拟书页内容,可以在此进行网络请求。
simulatePageContent() {
   this.leftPageContent = STRINGCONFIGURATION.PAGEFLIPRESOURCE + (this.currentPageNum - CONFIGURATION.PAGEFLIPPAGECOUNT).toString();
   this.midPageContent = STRINGCONFIGURATION.PAGEFLIPRESOURCE + (this.currentPageNum).toString();
   this.rightPageContent = STRINGCONFIGURATION.PAGEFLIPRESOURCE + (this.currentPageNum + CONFIGURATION.PAGEFLIPPAGECOUNT).toString();
}

工程结构&模块类型

   pageflip                                         // har包
   |---common
   |   |---Constants.ets                            // 常量 
   |---components
   |   |---mainpage                                 
   |       |---PageFlip.ets                         // 主页面
   |---datasource
   |   |---BasicDataSource.ets                      // Basic数据控制器
   |---view
   |   |---BottomView.ets                           // 底部菜单视图
   |   |---CoverFlipPage.ets                        // 覆盖翻页视图
   |   |---LeftRightFlipPage.ets                    // 左右翻页视图
   |   |---TopView.ets                              // 顶部菜单视图
   |   |---UpDownFlipPage.ets                       // 上下翻页视图

模块依赖

routermodule

高性能知识点

本例使用了onActionUpdate函数。该函数是系统高频回调函数,避免在函数中进行冗余或耗时操作,例如应该减少或避免在函数打印日志,会有较大的性能损耗。

本示例使用了LazyForEach进行数据懒加载,List布局时会根据可视区域按需创建ListItem组件,并在ListItem滑出可视区域外时销毁以降低内存占用。

本示例使用了cachedCount设置预加载的ListItem的数量,只在LazyForEach中生效,设置该属性后会缓存cachedCount个ListItem,LazyForEach超出显示和缓存范围的ListItem会被释放。

参考资料

LazyForEach:数据懒加载

ZIndex

List

Swiper

@Link装饰器:父子双向同步

最后分享一份鸿蒙(HarmonyOS)开发学习指南需要的可以扫码免费领取!!!

《鸿蒙(HarmonyOS)开发学习指南》

第一章 快速入门

1、开发准备

2、构建第一个ArkTS应用(Stage模型)

3、构建第一个ArkTS应用(FA模型)

4、构建第一个JS应用(FA模型)

5、…

图片

第二章 开发基础知识

1、应用程序包基础知识

2、应用配置文件(Stage模型)

3、应用配置文件概述(FA模型)

4、…

图片

第三章 资源分类与访问

1、 资源分类与访问

2、 创建资源目录和资源文件

3、 资源访问

4、…

图片

第四章 学习ArkTs语言

1、初识ArkTS语言

2、基本语法

3、状态管理

4、其他状态管理

5、渲染控制

6、…

图片

第五章 UI开发

1.方舟开发框架(ArkUI)概述

2.基于ArkTS声明式开发范式

3.兼容JS的类Web开发范式

4…

图片

第六章 Web开发

1.Web组件概述

2.使用Web组件加载页面

3.设置基本属性和事件

4.在应用中使用前端页面JavaScript

5.ArkTS语言基础类库概述

6.并发

7…

图片

11.网络与连接

12.电话服务

13.数据管理

14.文件管理

15.后台任务管理

16.设备管理

17…

图片

第七章 应用模型

1.应用模型概述

2.Stage模型开发指导

3.FA模型开发指导

4…

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内存函数:memcpy(拷贝),memmove(拷贝),memcmp(比较),memset(设置)

内存函数 一.memcpy&#xff08;内存拷贝1&#xff09;1.函数使用2.模拟实现 二.memmove&#xff08;内存拷贝2&#xff09;1.函数使用2.模拟实现 三.memcmp&#xff08;内存比较&#xff09;1.函数使用2.模拟实现 四.memset&#xff08;内存设置&#xff09;1.函数使用2.模拟实…

【Doris的安装与部署】

1 集群规划和环境准备 Doris作为一款MPP架构的OLAP数据库&#xff0c;可以在绝大多数主流的商用服务器上运行。 1.1 环境要求 一般推荐使用Linux系统&#xff0c;版本要求是CentOS 7.1及以上或者Ubuntu 16.04及以上&#xff0c;这也是目前服务器市场最主流的操作系统。 操作…

算法练习day4

前言 中间个人原因断了很久&#xff0c;现在回来继续。。。。 两两交换链表中的节点 代码随想录 两两交换链表中的节点 24. 两两交换链表中的节点 - 力扣&#xff08;LeetCode&#xff09; &#xff08;用时&#xff1a;0.3小时&#xff09; 思路 这道题的思路其实很简单…

[数据结构]红黑树的原理及其实现

文章目录 红黑树的特性红黑树的时间复杂度推导&#xff1a;结论红黑树与AVL树比较 红黑树的插入红黑树的节点定义调整策略思考情况2&#xff1a;思考情况3&#xff1a; 代码实现myBTRee.htest.cpp 红黑树的特性 红黑树最常用的平衡二叉搜索树。跟AVL树不同的是&#xff0c;红黑…

OpenAI 推出革命性新模型 GPT-4o:全能AI的新纪元

GPT-4o 模型的推出预示着人工智能领域的又一次飞跃&#xff0c;它将如何改变我们的世界&#xff1f; 在人工智能的快速发展浪潮中&#xff0c;OpenAI 再次站在了技术革新的前沿。2024年5月14日&#xff0c;OpenAI 宣布了其最新旗舰模型 GPT-4o&#xff0c;这不仅是一个简单的版…

GitHub操作

远程库-GitHub GitHub网址 GitHub是全球最大的远程库 1. 创建远程库 2. 远程仓库操作 2.1 创建远程仓库别名 git remote -v 查看当前所有远程库地址别名 git remote add 别名 远程地址 设置远程库地址别名 案例操作 起一个别名会出现两个别名&#xff0c;是因为既可以拉取…

艺人百度百科怎么创建

创建艺人百度百科是一个相对复杂的过程&#xff0c;需要遵循一定的步骤和规则。以下是百科优化网yajje整理的艺人百度百科创建指南&#xff1a; 了解百度百科的创建流程 在创建艺人百度百科页面之前&#xff0c;首先需要了解百度百科的创建流程。可以通过访问百度百科的官方网…

uniapp如何打包预约上门按摩APP

uniapp如何打包预约上门按摩APP&#xff1f; 开发工具&#xff1a;HBuilderX 一、创建移动应用 1、 点击此处微信开放平台 2、点击【管理中心 - 移动应用 - 创建移动应用】填写资料后等待审核 app运行流程图 签名如何获取&#xff1a; 1&#xff09;先把打包好的app安装在手…

防爆安检系统市场规模保持增长态势 行业将向智能化方向发展

防爆安检系统市场规模保持增长态势 行业将向智能化方向发展 防爆安检系统&#xff0c;是指为了防止爆炸物品及其他危险物品进入特定区域而设置的一套完整的设备系统&#xff0c;细分产品包括金属探测器、生物安全检测设备、爆炸物探测器等。防爆安检系统能够有效检测并识别出潜…

单文件组件,为什么要使用 SFC

介绍 Vue 的单文件组件 (即 *.vue 文件&#xff0c;英文 Single-File Component&#xff0c;简称 SFC) 是一种特殊的文件格式&#xff0c;使我们能够将一个 Vue 组件的模板、逻辑与样式封装在单个文件中。下面是一个单文件组件的示例&#xff1a; <script setup> impor…

【Qt】之【CMake】Error : The source.. does not match the soused

QT中cmak编译出现CMake Error: The source… does not match the soused 分析 前提是该项目是从另一个路径的项目复制过来的&#xff0c;编写代码时发现无论怎样修改代码&#xff0c;运行后都没有任何变化&#xff0c;以为是qtbug&#xff0c;重构重启都没用&#xff0c;最后…

Spring注解驱动开发

1、Spring注解驱动开发图解

旅游集市数仓建设

旅游集市数仓建设 小白如何从0到1成为大数据工程师 目录 旅游集市数仓建设 1.上传数据 2.可能用到的UDF函数 3.创建所需数据库及表 1&#xff09;ODS层 ①ods_oidd ②ods_wcdr ③ods_ddr ④ods_dpi 2&#xff09;DWD层 ①dwd_res_regn_mergelocation_msk_d ②dwm_s…

2024年5月面试知识点梳理

2024年5月面试知识点梳理 资料来源Java基础泛型注解异常反射SPI机制Java集合CollectionMap 并发基础线程并发关键字并发集合Lock核心类并发集合核心类原子类核心类线程池核心类ScheduledThreadPoolExecutorForkJoinPoolFokJoinTask JUC原子类: CAS, Unsafe和原子类详解JUC 工具…

【Linux】linux | 配置系统日志 | 安全日志 | 操作日志 | 登录日志

一、诉求 1、linux服务器开启日志功能&#xff0c;并记录10个月的登录 二、操作 1、进入目录 cd /etc 2、编辑配置 vi logrotate.conf 3、复制配置 /var/log/wtmp {monthlycreate 0664 root utmpminsize 1Mrotate 10 }/var/log/btmp {missingokmonthlycreate 0600 root …

多客陪玩系统,陪玩系统源码,线下搭子,爆改家政整理师等功能,陪玩预约系统 定制化陪玩系统,陪玩软件APP小程序H5游戏陪玩成品软件源码

简述 陪玩系统源码是指一款游戏陪玩平台的程序代码。陪玩系统通常是一个在线平台&#xff0c;可以让用户通过该平台找到愿意为他们提供游戏陪玩服务的人员&#xff0c;从而帮助他们在游戏中取得更好的成绩。这种系统通常包括客户端和服务器端两个部分&#xff0c;客户端用于用…

【强训笔记】day23

NO.1 思路&#xff1a;直接计算结果&#xff0c;先计算怪物可以抗几次攻击&#xff0c;再计算勇士受到的伤害&#xff0c;如果勇士的攻击力大于等于怪物的血量&#xff0c;那么就可以击杀无数只&#xff0c;如果勇士的血量正好是受到攻击的整数倍&#xff0c;那么击杀的怪物数…

服务攻防——应用协议软件,设备平台

向日葵利用 vnc利用5900端口 当为none就可以直接连接&#xff0c;而其他几种密码也能破解 可以使用hydna来尝试爆破 teamviewer(cve2020-13699) 让对方点击这个网站&#xff0c;就会 触发 zabbix 端口10051 cve2020 手工 点击这个 找到cookie 然后不需要密码就能进…

手心经常出汗、多汗是怎么回事?

点击文末领取揿针的视频教程跟直播讲解 手心爱出汗是一种怎样的体验&#xff1f; 手握着鼠标&#xff0c;一会儿就变得湿漉漉&#xff0c;小心翼翼拿着手机&#xff0c;防止它从满是汗的手掌滑出去……其实手心总出汗可能是一种病。 很多人都有手掌心出汗的症状。如果年轻健康…

【leetcode面试经典150题】-26. 删除有序数组中的重复项

26. 删除有序数组中的重复项 1 题目介绍1 个人解题思路1.1 解题代码1.2 思路解析 2、分析官方题解2.1 快慢双指针 1 题目介绍 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新…