从零开始开发纯血鸿蒙应用之实现起始页

从零开始开发纯血鸿蒙应用

一、前言

从本篇开始,将对 TxtEdit 的业务功能进行逐一实现,而业务功能是宿主在一个个具体的应用页面上的,所以,当务之急,便是将 TxtEdit 的主要页面、即用于编辑和浏览纯文本文件的页面进行实现。

目前,第一版实现中,主要页面有如下几个:

像那些用于说明app开发者信息,以及进行问题反馈等页面,个人认为属于非关键的主要页面,因此,在应用开发过程中,其优先级较低,可以放在最后去实现。

二、主要页面

TxtEdit 在版本 1.0.x 的主要页面,共有三套:

  • 应用起始页,由一个 page 文件组成
  • 内部文件处理页,由两个 page 文件组成,分别用于编辑和浏览应用的沙箱文件
  • 外部文件处理页,也是两个 page 文件组成,用于浏览和导入。

三、应用起始页

应用起始页,由 Index.ets 文件实现,页面路由为 pages/Index,它的代码内容结构如下:

根据 build 函数体,可知 TxtEdit 的起始页,和众多app的起始页一样,都是采用 Tabs 组件进行页面结构搭建,我这里,由于追求简洁和实用,Tabs 里面的 TabContent 也即所谓 Tab 页,只有两个。

在鸿蒙UI中,Tabs 组件具有横向纵向 两种方向,并且 tabBar 也即 Tab 页标题栏,允许传入一个注解 @Builder 的 UI 构建函数,从而实现更美观或更个性化的应用起始页,由于本人美术天赋较差,设计不出让人眼前一亮的 UI 样式,只有如下的简简单单的:

ts 复制代码
@Builder
tabBuilder(title: string, targetIndex: number) {
  Column() {
    Text(title)
      .fontColor(this.currentIndex === targetIndex ? '#ff0af10a' : '#fff5f6f5')
  }
  .width('100%')
  .height(60)
  .justifyContent(FlexAlign.Center)
}

只做了根据Tab页是否被选中进行Tab标题颜色的变更。

为了达到 Tabs 组件居底布局,页面标题居顶布局,我将 Tabs 套在一个使用了 BottomColumn 样式的 Column 文件中,再将这个 Column 放在一个使用 RootTopColumn 样式的 Column 文件中。

TxtEdit 的起始页中的第一个 Tab 页,即文件 Tab 页,顾名思义,用于展示文件列表,方便用户进行内部文件的操作和管理,这时候,涉及到无内部文件和有内部文件两种状态的页面内容,要以什么样的方式进行载入?

方案不外乎两种,一种是在 TabContent 里面直接做条件渲染,根据条件使用不同的Content ,另一种是TabContent 里面使用一个直接展示的 Content,将条件渲染下移到该Content里面

本人采取的就是第二种方案:

ts 复制代码
build() {
  Column(){
    PageTitleBar({ title: 'TxtEdit'})
    Column(){
      Tabs(){
        TabContent(){
          Column(){
            MainPageContent()
          }.attributeModifier(this.centerColumn)
        }.tabBar(this.tabBuilder('文件', 0))
        TabContent(){
          AboutPageContent()
        }.tabBar(this.tabBuilder('关于', 1))
      }.attributeModifier(this.blackTabs)
      .onChange((index) => {
        this.currentIndex = index
      })
    }.attributeModifier(this.bottomColumn)
  }.attributeModifier(this.rootContainer)
}

四、MainPageContent 实现

在我尚未叙述具体实现时,屏幕前的你,对于 MainPageContent 的实现,心里有什么样的方案?是否认为,用一个类似 if-else 的语法结构,就能够实现无文件页面和有文件页面的载入显示?

如果是这样,那么你大概没有考虑到,用户场景存在着一种情况,就是从有文件到无文件的状态回退。此外,实时的 UI 刷新,比如用户创建第一个文件结束后,回到起始页,应当将用户创建的文件展示在文件列表中,而不是要用户杀掉app进程重新进入才能看到。

总的来说,app页面的实现,并非是想当然而,需要将可能的动态刷新场景兜底住,才能给用户一个较好的UI体验和交互操作体验。

1、一级结构

本篇后续,以及后面的每一篇博文,为了控制篇幅,主要是内容字符数,源码会贴的比较少,更多以图片的形式展示,完整源码还请访问gitee仓库获取,当然了,gitCode仓库也是准备了的。

由简入繁,先看一下函数体都收起时的内容。整体上,在 MainPageContent 中,最开始的地方,定义了一组辅助 UI 刷新的私有字段或状态变量,其中,状态变量是触发 UI 刷新所不可或缺的。

在页面即将渲染时,也即生命周期函数 aboutToAppear 所对应的阶段,需要做一些初始化工作:

利用项目内部 API,即 FileUtil 去获取内部文件的文件名列表,并且根据列表的空与非空,决定最终渲染的内容是 EmptyContent 还是 FileListContent,我这里所采用的条件渲染控制,并非鸿蒙UI所支持的 if-else 语法,而是组件属性 visibility,如此做法,其考量就是让UI代码更纯粹些

为了满足实时刷新UI内容的需要,doRefresh 函数做了如下工作:

虽然,和 aboutToAppear 很像,却也是必不可少的,毕竟已经通过装饰器 @Watch 与 refresh 做了绑定,而 refresh 实际上是一个应用全局的UI状态存储 中的字段,利用应用全局的UI状态存储机制,可以做到在B页面控制A页面进行UI更新,具体用法后面会给出。

2、二级结构

MainPageContent 的二级结构,实际对应的就是 EmptyContent 和 FileListContent。鉴于 EmptyContent 的实现更为简单,所以分析顺序就从 EmptyContent 开始。

2.1、EmptyContent

如图所示,开始处也是一批字段的定义,其中的 dialog 字段实际是一个 自定义弹窗

通过代入 CustomDialogController 的参数 alignment ,将弹窗设置为页面居中显示,这里需要提醒一下 DevEco Studio 的 UI 预览器和真机显示上,在自定义弹窗上的区别,预览器显示弹窗是默认居中的,但真机则不是如此;另一个带入 CustomDialogController 的参数,用于控制是否允许自动关闭弹窗,即点击弹窗外的区域时是否允许关闭弹窗,显然,我这里设置为不允许,因此,弹窗的关闭只能通过显示在弹窗上的两个按钮去进行:

这个弹窗的打开动作,我将它放在"新建或导入按钮"中。

2.2、FileListContent

FileListContent 的结构,与 EmptyContent 的结构很相似,只不过"新建或导入"按钮上方,变成了一个列表组件。列表组件的列表项,根据fileList也即内部文件名列表动态生成,每个列表项都是由 RecentFileItem 实现:

RecentFileItem 的实现如下:

其显示效果如图:

点击"操作"文本,会有弹出式菜单显示出来,该菜单提供四个操作:查看、编辑、分享和删除。弹出式菜单的使用,只需在鸿蒙UI组件的bindMenu传入菜单绘制方法即可,也就是这里的 OptionMenu 方法:

四个操作的响应逻辑,通过 RecentFileItem 的四个函数参数载入,每一个具体实现逻辑,下面一一介绍:

2.2.1、ViewAction:

该动作的含义,就是浏览当前列表项对应的沙箱文件 ,实现代码比较简单,就是将对应的沙箱文件名通过路由参数带入到 ViewFilePage 中。

2.2.2、EditAction

动作含义就是编辑当前列表项对应的沙箱文件,由于实现代码与 ViewAction 类似,便不过多赘述。

2.2.3、DeleteAction

动作含义就是删除当前列表项对应的沙箱文件 ,在实现方面,主要用项目内部 API FileUtil.deleteFile(this.ctx, item) 实现文件的删除,而在删除成功的回调处理中,会对 TxtEdit 的起始页进行 UI 更新,即当最后一个内部文件被删除时,起始页重新显示无文件对应的页面内容;否则仍旧显示文件列表

2.2.4、ShareAction

该动作的含义,就是分享当前列表项对应的沙箱文件 ,在具体实现方面,也是用了一个项目内部 API,其都对应的具体代码如下:

主要就是利用鸿蒙系统提供的系统分享 面板进行文件的分享:

要呼出如上的系统分享面板,需要做以下几方面的工作:

1)判断要分享的文件是否存在,避免引起其他应用出现异常

2)获取待分享文件对应的 utdTypeId

3)借助 systemShare.SharedData 包装分享内容,因为系统分享面板只接受 systemShare.SharedData 的数据

4)创建并持有 systemShare.ShareController,即系统分享面板控制器

5)借助系统分享面板控制器拉起系统分享面板,并对是否成功拉起面板进行回调处理

五、载入起始页的时机

在鸿蒙应用开发框架中,应用起始页的载入时机为,应用声明周期函数 onWindowStageCreate

在该函数中,会通过系统 API windowStage.loadContent 去加载某个 Page 作为起始页,而这里就是 'pages/Index'

五、总结

正所谓台上一分钟台下十年功,看起来很简单的应用起始页,实现起来往往需要对多种动态刷新和操作交互进行处理,更需要保证页面间的数据流透传方向不出错、以及应用全局范围的信号起作用等。

相关推荐
i200212093 小时前
【HarmonyOS】纯血鸿蒙真实项目开发---经验总结贴
华为·harmonyos·鸿蒙·鸿蒙系统
m0_726965985 小时前
Harmony开发【笔记1】报错解决(字段名写错了。。)
笔记·bug·debug·harmonyos
别说我什么都不会8 小时前
OpenHarmony图形处理库—pyclipper [GN编译]
harmonyos·opengl
AIGC方案9 小时前
华为IPD成功案例分析
华为
塞尔维亚大汉10 小时前
移植案例与原理 - utils子系统之KV存储部件 (3)
操作系统·harmonyos
goodbruce10 小时前
HarmonyOS鸿蒙开发 弹窗及加载中指示器HUD功能实现
harmonyos
轻口味11 小时前
【每日学点鸿蒙知识】调试、网络、缓存、富文本编辑等
缓存·华为·harmonyos
MarkHD11 小时前
第一天 了解HarmonyOS的起源、发展、核心特性
华为·harmonyos
执着的小火车12 小时前
【2024华为OD-E卷-100分-boss的收入】(题目+思路+Java&C++&Python解析)
数据结构·算法·华为od·华为·排序算法