从零开始开发纯血鸿蒙应用
- 一、前言
- 二、EditPage
-
- 1、文件保存对话框
- [2、EditPage 初始化](#2、EditPage 初始化)
- 2、实现内容编辑功能
- 3、避让键盘
- 三、ViewFilePage
- 四、总结
一、前言
在上一篇,我将 TxtEdit 的起始页的实现进行了分享,现在,接着分享 内部文件处理页
的实现。
内部文件与外部文件的最大区别,在于内部文件的存储位置,是明确而固定的,因此,只要有文件名便可以进行文件的创建、再编辑、浏览与删除,而无需完整的沙箱URI,这一点是外部文件所无法具有的。
内部文件处理页,分为 EditPage 和 ViewFilePage,前者用于文件的新建和再编辑,后者用于文件的只读浏览。
二、EditPage
首先,看一下代码的整体内容:
1、文件保存对话框
遵循从上到下的顺序,先看一下文件保存对话框 的具体实现:
这次使用自定义弹窗样式,与之前的 MessageDialog 不一样,是一个专门用于文件保存的 SaveFileDialog,并且会通过函数参数 confirm 载入文件保存逻辑,其具体的实现代码如下:
这里,重点关注一个新的鸿蒙组件TextPicker
的使用,这是一个文本选择器 ,大家在填写地址信息时见到的,从一组候选省市区选择出目标省市区的操作,在鸿蒙app中,就可以用这个组件去实现:
TextPicker 组件,针对候选文本的选中、未选中和消失状态,分别提供属性selectedTextStyle
、textStyle
和 disappearTextStyle
进行样式定义。
文件名的填写和文件后缀的选择,都是在 SaveFileDdialog 中,然而,文件保存逻辑的实现代码,却是在它的父级组件中,那么问题就来了,子组件更新的数据如何回传到父组件中呢?ArkTS 语言又不是 C/C++,参数传递并不存在引用传递类型,都是值传递类型,但这也只是针对普通字段而言的。
鸿蒙UI框架,为了满足数据在父子组件的单向传递
或双向传递
,提供了一组状态装饰器:
在 SaveFileDialog 里面,就用状态装饰器 @Link
,去完成文件名和文件后缀的回传。
在父组件也即 EditPage 中,确认按钮的处理逻辑由下面几个步骤构成:
1)判断文件名是否不为空,不为空才进行后续步骤,否则就 Toast 提示用户输入文件名
2)判断待保存的文件内容是否不为空,如果为空则 Toast 提示,反之则进入文件保存逻辑
3)在保存文件前,会根据用户输入的文件名和选择的文件后缀,拼接出完整的 Filename,并调用 FileUtil.saveToFile 进行保存
4)在文件保存成功时,弹出 Toast 提示,并设置文件列表刷新信号,然后回到起始页。
2、EditPage 初始化
在 位于 dialog 字段下方的 aboutToAppear 函数中,进行页面进入类型的判断,即根据路由是否携带非空的文件名,判断当前进入是文件再编辑还是文件新建:
当进入动作属于文件再编辑,也即路由携带了文件名,就会进行对应文件的内容读取,并显示在 TextArea 中,供用户再次编辑文件内容。
2、实现内容编辑功能
对于文件这种涉及多行文本编辑的场景,实现功能所用的组件,不应该是 TextInput
,必须是 TextArea
,同时,为了方便用户在完成内容编辑后收起键盘,应该提供类似点击空白区域收起输入法的功能 :
自动收起键盘,其实,就是要动态设置 TextArea 的 focusable
属性,即是否允许组件获取焦点
,当 TextArea 被设置为不允许获得焦点,自然就不能进入编辑状态,输入法也就自然会收起软键盘,接着只需通过一个倒数时间较短的 setTimeout 回调恢复 TextArea 允许获得焦点,便能恢复编辑能力。
由于 TextArea 占据了页面标题之后,到输入法软键盘为止的屏幕区域,因此,我将触发输入法收起软键盘的点击区域,放在了页面标题栏上。说到页面标题栏,需要载入页面返回功能和菜单交互逻辑:
由于,暂时还没想好 EditPage 应该提供什么样的右上角菜单,所以,右上角菜单的动作处理,只是弹出 Toast 提示。
3、避让键盘
由于 EditPage 是编辑内容的页面,因此,需要处理避让键盘
的问题,如果不进行避让键盘处理,那么,当文件内容比较多时,就会出现这种情况:
位于页面上方的标题栏被输入法的软键盘,给顶到屏幕可视区域外,看起来相当不好看,而加入避让键盘处理后,就不会出现这种情况:
那么,避让键盘如何实现呢?
在 API 12 之后,只需要在页面的根容器上,用属性 expandSafeArea
设置即可:
三、ViewFilePage
ViewFilePage 的实现代码,整体内容如下:
1、页面初始化
由于是文件内容浏览页,因此,需要在页面渲染之前,将对应的文件内容准备好:
2、条件渲染
文件即便存在,也不意味着内容也存在,所以,当文件内容为空的时候,直接展示一个提示信息即可:
当文件内容不为空时,就需要稍微复杂且允许一定交互动作的 UI 实现代码,比如,当文件内容比较多,超出了屏幕可视区域,那么就要支持滑动交互,以便将屏幕外的内容滚进屏幕里:
此外,用户在浏览文件内容的时候,往往会有选择复制文本的习惯,因此,位于 Scroll 组件内的、用于展示文件内容的 Text 组件,我用上了新的属性和事件。
3、copyOption属性
Text 组件要具备文本选择能力和文本复制能力,需要设置 copyOption
属性和事件 onCopy
,至于 onTextSelectionChange
事件的捕获,在这里,仅仅是为了日志的打印。
3.1、合法候选值
该属性用于设置被复制的文本,在什么样 的范围内是有效、或者说是可用的,在 API 13 中,Text 组件的 copyOption 属性的合法候选值有如下:
我这里设置为 LocalDevice,如此,被复制的文本才能在聊天app,如畅连、微信以及QQ等应用的聊天信息输入框中进行使用。
这里需要强调一点的是,Text 组件所展示的文本的复制,并不需要开发者在 onCopy 事件处理函数中,手动往系统剪贴板插入记录,鸿蒙系统自动会完成剪贴板记录的新增,所以,如果你不需要通过日志去记录用户的复制动作,onCopy 事件完全可以不进行捕获的。
3.2、默认值
Text 组件的 copyOption 属性,具有默认值:
该默认值为 None,也即不允许复制,所以,鸿蒙app内禁止内容复制,对于文本来说,保持 Text 组件 copyOption 属性的默认值即可。
四、总结
实现上述两个页面后,TxtEdit 的业务功能基本实现了百分之五十,除了还无法处理外部文件,内部文件的创建、再编辑和浏览,乃至分享和删除,都已经具备了。
EditPage 的渲染效果,如下:
ViewFilePage 的则如下: