纯血鸿蒙APP实战开发——阅读翻页方式案例

news2024/11/15 17:17:10

介绍

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

效果图预览

使用说明

  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方法。
 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方法。
// 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相应的内容。
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装饰器:父子双向同步

鸿蒙全栈开发全新学习指南

也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大厂APP实战项目开发】

本路线共分为四个阶段:

第一阶段:鸿蒙初中级开发必备技能

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:https://gitee.com/MNxiaona/733GH

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

鸿蒙入门教学视频:

美团APP实战开发教学:gitee.com/MNxiaona/733GH

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH

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

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

相关文章

显影不干净如何解决?

知识星球&#xff08;星球名&#xff1a;芯片制造与封测社区&#xff0c;星球号&#xff1a;63559049&#xff09;里的学员问&#xff1a;光刻工序完成后&#xff0c;晶圆表面有部分图形容易出现显影不净是什么原因&#xff1f;有什么好的解决办法吗&#xff1f; 光刻工序流程 …

武汉星起航引领跨境新浪潮,一站式解决方案助力卖家驰骋亚马逊

在全球化浪潮下&#xff0c;跨境电商已成为外贸发展的新引擎&#xff0c;为无数创业者提供了全新的商业机遇。而在这场跨境电商的浪潮中&#xff0c;武汉星起航电子商务有限公司以其专业的一站式解决方案&#xff0c;成为众多创业者和卖家的得力助手&#xff0c;引领着他们成功…

信息系统项目管理师计算题讲解,考前必看

信息系统项目管理师考试中的计算题主要涉及项目管理类的计算&#xff0c;重点在于进度和成本的计算。案例计算题所占分值较高&#xff0c;务必要熟练掌握&#xff01;根据近年考题内容来看&#xff0c;主要涉及挣值计算和网络图&#xff0c;当然也可能会涉及其他内容&#xff0…

图片转pdf的java代码实现

一、实现方式 采用itextpdf和itext包&#xff0c;使用java代码&#xff0c;把图片转换为pdf. 支持文件格式&#xff1a;png&#xff0c;jpg, jpeg,gif 二、java代码实现 1、maven依赖 <!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf --><dependen…

【https】怎么免费实现https!

一、简介 实现 HTTPS&#xff08;Hyper Text Transfer Protocol Secure&#xff09;通常需要购买和配置 SSL/TLS 证书&#xff0c;这是确保网站或应用程序安全传输数据的关键步骤。然而&#xff0c;有一些方法可以在某些情况下“免费”地实现 HTTPS&#xff0c;但这通常涉及到…

重生奇迹mu再生宝石怎么用有什么用

重生奇迹mu再生宝石有2个用处&#xff1a; 1、在玛雅哥布林处给380装备加PVP属性4追4以上的380级装备,守护宝石一颗,再生宝石一颗,成功得到PVP装备,失败宝石消失,装备无变化&#xff1b; 2、给非套装点强化属性用法跟祝福,灵魂,生命一样直接往装备上敲,成功得到随机强化属性一…

Python框架Django入门教程

Django 是一个使用 Python 编程语言开发的、免费且开源的 Web 应用框架。它遵循 "DRY&#xff08;Dont Repeat Yourself&#xff09;" 原则&#xff0c;旨在简化创建功能丰富的、高效率的 Web 网站。Django 提供了模型-视图-控制器&#xff08;MVC&#xff09;架构的…

Bpmn.js使用(仅查看版)

Bpmn.js使用&#xff08;仅查看版&#xff09; 下载 npm install bpmn-js创建一个 Dom 节点来挂载画布元素。 <a-tabs v-model:activeKey"activeKey" change"tabsChange"><a-tab-pane key"1" tab"审批记录"><a-tabl…

LLM 可以从简单数据中学习吗?

在 10 月份的一次周会结束后&#xff0c;我提到 SFT 训练后的 Loss 曲线呈现阶梯状&#xff0c;至于为什么&#xff0c;并没有人有合理的解释&#xff0c;加上当时的重心是提升次日留存率&#xff0c;Loss 曲线呈现阶梯状与次日留存率的关系还太远&#xff0c;即使有问题&#…

99、技巧-下一个排列

这个问题要求生成一个数组的下一个排列。所谓“下一个排列”指的是&#xff0c;在所有数字相同但顺序不同的排列中&#xff0c;找出数字序列中刚好比当前序列大的下一个序列。如果当前序列已经是这些排列中的最大值&#xff0c;则下一个排列应该是最小的排列。 思路解释 要解…

【CTFHub】HTTP 请求方式 302跳转 cookie WP

1.请求方式 打开给出的URL进入一个页面&#xff0c;提示原方法是GET&#xff0c;用CTFHUB方法就能获得flag 思路&#xff1a;抓包&#xff0c;将GET方法改成CTFHUB方法进行重新发送请求&#xff0c;查看响应情况 1.打开代理服务器 2.打开BurpSuite 刷新页面获得拦截 3.发送…

天猫最热销的三款随身WiFi,哪一款直播最好用?2024公认最好的随身WiFi,天猫上的随身wifi是正规产品吗

近期有小伙伴问我&#xff1a;“小编、小编我要当户外博主了&#xff0c;想买一个随身WiFi&#xff0c;但是天猫榜单前三的随身WiFi自己都没有听说过&#xff0c;到底入手哪个比较好&#xff1f;”三款随身WiFi呢&#xff0c;分别是格行随身WiFi、迅优随身WiFi、小米随身WiFi&a…

Minimal-Supervised Medical Image Segmentation via Vector Quantization Memory

文章目录 Minimal-Supervised Medical Image Segmentation via Vector Quantization Memory摘要方法实验结果 Minimal-Supervised Medical Image Segmentation via Vector Quantization Memory 摘要 辅助重构分支&#xff1a;该分支通过提供额外的监督并产生学习视觉表示所需…

【JavaEE精炼宝库】多线程1(认识线程 | 创建线程 | Thread 类)

目录 一、认识线程 1.1 线程的概念&#xff1a; 1.2 为什么需要线程&#xff1a; 1.3 面试题.谈谈进程和线程的区别&#xff1a; 1.4 Java的线程和操作系统线程的关系&#xff1a; 二、创建线程 2.1 创建线程的5种写法&#xff1a; 2.1.1 写法1.继承 Thread 类&#xf…

JS实现初始化、动态点击切换激活样式

食用须知&#xff0c;不懂得把代码交给AI解释一下&#xff0c;明白流程就会用了&#xff0c;本文只有js与html&#xff0c;样式代码一概没有&#xff1a; 效果展示 1、点击显示的盒子代码 <div data-v-e1dd37c4"" class"news-container main-width-contain…

uniapp 版本检查更新

总体来说uniapp的跨平台还是很不错的&#xff0c;虽然里面各种坑要去踩&#xff0c;但是踩坑也是开发人员的必修课和成长路。 这不&#xff0c;今天就来研究了一下版本检查更新就踩到坑了。。。先来看看检查更新及下载、安装的实现。 先来看看页面&#xff1a; 从左到右依次为…

【Linux系统编程】第十六弹---冯诺依曼体系结构与操作系统

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、冯诺依曼体系结构 2、操作系统原理 2.1、什么是操作系统&#xff1f; 2.2、用图解释操作系统 2.3、理解操作系统 总结 …

瞪羚企业申报条件!武汉市瞪羚企业奖励补助

武汉市江岸区、江汉区、硚口区、汉阳区、武昌区、青山区、洪山区、蔡甸区、江夏区、黄陂区、新洲区、东西湖区、汉南区企业申报瞪羚企业的条件是什么&#xff1f;各区对瞪羚企业的奖励补助有哪些&#xff1f; 武汉市瞪羚企业申报条件&#xff1a; 1、符合武汉市光电子信息、生物…

Windows Server 2012 R2 新增D盘分区

我们经常搭建windows版本的游戏时会要在D盘上操作&#xff0c;今天就介绍下新的服务器如何新增一个D盘。 在"开始"图标右边有个”服务器管理器“&#xff0c;单击点开 点开服务器管理器后&#xff0c;点击“工具”打开“计算机管理” 打开计算机管理后点击“存储”-…

发票审核如何自查?报销没有发票,如何处理?

在财务管理中&#xff0c;发票是非常重要的一项凭证&#xff0c;是费用核算和税务申报的重要依据&#xff0c;但光靠发票入账可能会被定义为虚开。 一、费用报销审核必看的6个要点 1、票据与实际业务吻合 这是费用报销中最基本的常识&#xff0c;比如&#xff1a;采购一批物料&…