Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
Docs
提交
adaba53c
D
Docs
项目概览
OpenHarmony
/
Docs
大约 2 年 前同步成功
通知
161
Star
293
Fork
28
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
Docs
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
adaba53c
编写于
6月 28, 2023
作者:
D
duangavin123
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
新增案例
Signed-off-by:
N
duangavin123
<
duanxichao@huawei.com
>
上级
b49b0f3d
变更
10
隐藏空白更改
内联
并排
Showing
10 changed file
with
1754 addition
and
0 deletion
+1754
-0
zh-cn/third-party-cases/Readme-CN.md
zh-cn/third-party-cases/Readme-CN.md
+7
-0
zh-cn/third-party-cases/delete-checkboxgroup-items.md
zh-cn/third-party-cases/delete-checkboxgroup-items.md
+357
-0
zh-cn/third-party-cases/figures/Pixel-Convertion.gif
zh-cn/third-party-cases/figures/Pixel-Convertion.gif
+0
-0
zh-cn/third-party-cases/figures/checkbox-after-improve.PNG
zh-cn/third-party-cases/figures/checkbox-after-improve.PNG
+0
-0
zh-cn/third-party-cases/figures/checkbox-before-improve.PNG
zh-cn/third-party-cases/figures/checkbox-before-improve.PNG
+0
-0
zh-cn/third-party-cases/figures/delete-checkboxitem.gif
zh-cn/third-party-cases/figures/delete-checkboxitem.gif
+0
-0
zh-cn/third-party-cases/figures/preference-storage.gif
zh-cn/third-party-cases/figures/preference-storage.gif
+0
-0
zh-cn/third-party-cases/image-format-transfer.md
zh-cn/third-party-cases/image-format-transfer.md
+225
-0
zh-cn/third-party-cases/pixel-format-transfer.md
zh-cn/third-party-cases/pixel-format-transfer.md
+814
-0
zh-cn/third-party-cases/preferences-data-process.md
zh-cn/third-party-cases/preferences-data-process.md
+351
-0
未找到文件。
zh-cn/third-party-cases/Readme-CN.md
浏览文件 @
adaba53c
...
@@ -26,6 +26,9 @@
...
@@ -26,6 +26,9 @@
-
[
常见弹窗的使用
](
diverse-dialogues.md
)
-
[
常见弹窗的使用
](
diverse-dialogues.md
)
-
[
折叠展开动效
](
collapse-and-expand.md
)
-
[
折叠展开动效
](
collapse-and-expand.md
)
-
[
列表上拉加载更多内容
](
list-pullup-loading-data.md
)
-
[
列表上拉加载更多内容
](
list-pullup-loading-data.md
)
-
[
如何删除多选框选项
](
delete-checkboxgroup-items.md
)
-
[
像素单位转换
](
pixel-format-transfer.md
)
### 装饰器
### 装饰器
-
[
控制页面刷新范围
](
overall-and-part-refresh.md
)
-
[
控制页面刷新范围
](
overall-and-part-refresh.md
)
-
[
如何监听多层状态变化
](
observed-and-objectlink.md
)
-
[
如何监听多层状态变化
](
observed-and-objectlink.md
)
...
@@ -38,8 +41,12 @@
...
@@ -38,8 +41,12 @@
-
[
如何创建悬浮窗
](
float-window.md
)
-
[
如何创建悬浮窗
](
float-window.md
)
-
[
保持屏幕常亮
](
keep-screen-on.md
)
-
[
保持屏幕常亮
](
keep-screen-on.md
)
### 数据管理
-
[
用户首选项的基本使用
](
preferences-data-process.md
)
### 媒体
### 媒体
-
[
常见图片编辑
](
image-edit.md
)
-
[
常见图片编辑
](
image-edit.md
)
-
[
图片格式转换
](
image-format-transfer.md
)
### 一次开发,多端部署
### 一次开发,多端部署
-
[
Navigation如何实现多场景UI适配
](
multi-device-app-dev.md
)
-
[
Navigation如何实现多场景UI适配
](
multi-device-app-dev.md
)
...
...
zh-cn/third-party-cases/delete-checkboxgroup-items.md
0 → 100644
浏览文件 @
adaba53c
# 如何删除多选框选项
## 场景说明
通常情况下,我们使用多选框都会伴随对选项的操作,比较常见的操作是选中后删除,比如删除购物车的商品、删除账单、删除图片等等。但是,当前OpenHarmony针对多选框组件并没有提供直接的删除其选项的方法,需要开发者自己来实现。本例提供了一种实现方案,供开发者参考。
## 效果呈现
本例最总效果如下:

## 运行环境
本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:
-
IDE: DevEco Studio 4.0 Beta1
-
SDK: Ohos_sdk_public 4.0.7.5 (API Version 10 Beta1)
## 实现思路
本例中涉及的关键特性及实现方案如下:
-
多选框:使用CheckboxGroup组件构建多选框标题,使用Checkbox组件构建多选框选项。
-
删除多选框选项:通过CheckboxGroup的onChange回调获取到各个选项的选中状态,在删除操作中,将选中的选项从选项列表中删除。
-
删除时弹出确认框:使用promptAction模块调用showDialog方法弹出对话框,通过回调获取到用户点击的是取消按钮还是确定按钮,如果是确定按钮则执行删除操作。
## 开发步骤
1.
搭建UI布局。
整体纵向布局,那就采用Column组件;全选框用CheckboxGroup组件,然后每个选项都包括一个选择框(Checkbox组件)和一个文本(Text组件),且为横向布局,那我们可以把它们放在Flex组件中;最后是一个Button组件。这样整体布局就有了,具体代码如下:
```
ts
@
Component
struct
CheckboxDemo
{
build
(){
Column
(){
Flex
({}){
CheckboxGroup
({})
Text
(
'
水果清单
'
)
}
Flex
({}){
Checkbox
({})
Text
(
'
苹果
'
)
}
Flex
({}){
Checkbox
({})
Text
(
'
菠萝
'
)
}
Flex
({}){
Checkbox
({})
Text
(
'
柚子
'
)
}
Button
(
'
删除
'
)
}
}
}
```
框架搭好了,看下效果:

可以看到主选框和选项对齐了,接下来我们来调整下样式。
先给CheckboxGroup取个名字:fruit_list,然后为各个Checkbox添加相同的group名称,这样就可以将Checkbox挂靠到CheckboxGroup下,剩下的就是给各个组件添加margin、fontSize等通用属性了,不清楚各个组件有哪些属性的请自行查阅组件参考,具体代码如下:
```ts
@Component
struct CheckboxDemo{
build(){
Column(){
Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
// 将CheckboxGroup命名为'fruit_list'
CheckboxGroup({group: 'fruit_list'})
.selectedColor('#007DFF')
Text('水果清单')
.margin({right:20})
.fontSize(14)
.lineHeight(20)
.fontColor('#182431')
.fontWeight(FontWeight.Bold)
}
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
// 通过group参数将Checkbox挂到CheckboxGroup下
Checkbox({name:'苹果',group:'fruit_list'})
.selectedColor('#007DFF')
Text('苹果')
.margin({right:20})
.fontSize(14)
.lineHeight(20)
.fontColor('#182431')
.fontWeight(500)
}
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
Checkbox({name:'菠萝',group:'fruit_list'})
.selectedColor('#007DFF')
Text('菠萝')
.margin({right:20})
.fontSize(14)
.lineHeight(20)
.fontColor('#182431')
.fontWeight(500)
}
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
Checkbox({name:'柚子',group:'fruit_list'})
.selectedColor('#007DFF')
Text('柚子')
.margin({right:20})
.fontSize(14)
.lineHeight(20)
.fontColor('#182431')
.fontWeight(500)
}
Button('删除')
.margin({top:20,left:35})
.fontSize(15)
.padding({top:5,bottom:5,left:15,right:15})
}
}
}
```
我们再来看下效果,发现多选框的布局已经正常了:

2.
简化代码。
当前代码重复性很高,包含Checkbox的三个Flex的结构完全一样,我们可以简化一下。把重复的结构通过@Builder抽取出来作为一个自定义组件,用到的地方进行引用即可;每个Flex呈现的内容不同,那就将不同的内容作为参数传入。具体代码如下:
```
ts
@
Component
struct
CheckboxDemo
{
// flexNameList存储checkbox选项的文本内容;使用@State修饰后,flexNameList发生变化,UI会同步刷新
@
State
flexNameList
:
string
[]
=
[
'
苹果
'
,
'
菠萝
'
,
'
柚子
'
]
@
Builder
// 将重复内容封装成flexItem,通过参数checkboxName为Text组件传入显示文本,通过groupName绑定CheckboxGroup
flexItem
(
checkboxName
:
string
,
groupName
:
string
){
Flex
({
justifyContent
:
FlexAlign
.
Center
,
alignItems
:
ItemAlign
.
Center
})
{
Checkbox
({
name
:
checkboxName
,
group
:
groupName
})
.
selectedColor
(
'
#007DFF
'
)
Text
(
checkboxName
)
.
margin
({
right
:
20
})
.
fontSize
(
14
)
.
lineHeight
(
20
)
.
fontColor
(
'
#182431
'
)
.
fontWeight
(
500
)
}.
margin
({
left
:
36
})
}
build
(){
Column
(){
Flex
({
justifyContent
:
FlexAlign
.
Center
,
alignItems
:
ItemAlign
.
Center
}){
CheckboxGroup
({
group
:
'
fruit_list
'
})
.
selectedColor
(
'
#007DFF
'
)
Text
(
'
水果清单
'
)
.
margin
({
right
:
20
})
.
fontSize
(
14
)
.
lineHeight
(
20
)
.
fontColor
(
'
#182431
'
)
.
fontWeight
(
FontWeight
.
Bold
)
}
// 通过ForEach遍历flexNameList循环渲染生成UI
ForEach
(
this
.
flexNameList
,(
item
:
any
)
=>
{
// 引用封装的flexItem模板
this
.
flexItem
(
item
,
'
fruit_list
'
)
})
Button
(
'
删除
'
)
.
margin
({
top
:
20
,
left
:
35
})
.
fontSize
(
15
)
.
padding
({
top
:
5
,
bottom
:
5
,
left
:
15
,
right
:
15
})
}.
alignItems
(
HorizontalAlign
.
Center
)
}
}
```
3.
添加删除逻辑。
本例中是通过以下方式实现删除操作的:将选中的水果项从flexNameList中删除(为方便展示,本文直接将数据存储在数组中,实际开发需要对接数据库),由于flexNameList被@State修饰,所以其发生变化时会重新执行Build(),从而完成UI刷新,展示删除后的选项。
要实现上述逻辑,首先需要获取到被选中的水果项。这里可以通过CheckboxGroup的onChange回调获取,当选项的状态发生变化时,会触发回调并返回各选项的选中状态。
具体代码如下:
```
ts
@
Component
struct
CheckboxDemo
{
...
// itemStatus用来存储各选项的选中状态
private
itemStatus
:
CheckboxGroupResult
...
build
(){
Column
(){
Flex
({
justifyContent
:
FlexAlign
.
Center
,
alignItems
:
ItemAlign
.
Center
}){
CheckboxGroup
({
group
:
'
fruit_list
'
})
.
selectedColor
(
'
#007DFF
'
)
// 选项状态发生变化时触发onChange回调,各选项的选中状态储存在itemName中并返回,通过itemStatus.name可以获取到被选中的选项列表
.
onChange
((
itemName
:
CheckboxGroupResult
)
=>
{
this
.
itemStatus
=
itemName
})
...
}.
alignItems
(
HorizontalAlign
.
Center
)
}
}
```
将各选项的选中状态存储到itemStatus后,我们就可以通过Button触发删除操作了。当点击删除按钮时,触发删除操作,所以给Button添加onClick事件,并添加删除逻辑。代码如下:
```
ts
@
Component
struct
CheckboxDemo
{
......
// itemStatus用来存储各选项的选中状态
private
itemStatus
:
CheckboxGroupResult
......
build
(){
Column
(){
Flex
({
justifyContent
:
FlexAlign
.
Center
,
alignItems
:
ItemAlign
.
Center
}){
CheckboxGroup
({
group
:
'
fruit_list
'
})
.
selectedColor
(
'
#007DFF
'
)
// 选项状态发生变化时触发onChange回调,各选项的选中状态储存在itemName中并返回,通过itemStatus.name可以获取到被选中的选项列表
.
onChange
((
itemName
:
CheckboxGroupResult
)
=>
{
this
.
itemStatus
=
itemName
})
......
Button
(
'
删除
'
)
.
margin
({
top
:
20
,
left
:
35
})
.
fontSize
(
15
)
.
padding
({
top
:
5
,
bottom
:
5
,
left
:
15
,
right
:
15
})
// 点击触发删除操作
.
onClick
(()
=>
{
// 被选中的项存储在this.itemStatus.name列表中
for
(
let
i
of
this
.
itemStatus
.
name
){
// 从flexNameList中删除被选中的项,刷新UI
this
.
flexNameList
.
splice
(
this
.
flexNameList
.
indexOf
(
i
),
1
)
}
})
}.
alignItems
(
HorizontalAlign
.
Center
)
}
}
```
4.
添加删除确认框。
使用promptAction模块调用showDialog方法弹出对话框,然后将删除操作绑定到对话框的确定按钮,具体代码如下:
```
ts
// 导入promptAction模块
import
promptAction
from
'
@ohos.promptAction
'
;
@
Component
struct
CheckboxDemo
{
......
// itemStatus用来存储各选项的选中状态
private
itemStatus
:
CheckboxGroupResult
......
build
(){
Column
(){
Flex
({
justifyContent
:
FlexAlign
.
Center
,
alignItems
:
ItemAlign
.
Center
}){
CheckboxGroup
({
group
:
'
fruit_list
'
})
.
selectedColor
(
'
#007DFF
'
)
// 选项状态发生变化时触发onChange回调,各选项的选中状态储存在itemName中并返回,通过itemStatus.name可以获取到被选中的选项列表
.
onChange
((
itemName
:
CheckboxGroupResult
)
=>
{
this
.
itemStatus
=
itemName
})
......
Button
(
'
删除
'
)
.
margin
({
top
:
20
,
left
:
35
})
.
fontSize
(
15
)
.
padding
({
top
:
5
,
bottom
:
5
,
left
:
15
,
right
:
15
})
// 点击触发删除操作
.
onClick
(()
=>
{
// 调用对话框
promptAction
.
showDialog
({
title
:
''
,
message
:
'
确定删除吗?
'
,
buttons
:[
{
text
:
'
取消
'
,
color
:
'
#000000
'
},
{
text
:
'
确定
'
,
color
:
'
#000000
'
}
]
})
// 用户选择通过data回传,当data.index为1时,用户选择确定,当data.index为0时,用户选择取消
.
then
(
data
=>
{
// 当用户选择确定时,进行删除操作
if
(
data
.
index
===
1
){
// 被选中的项存储在this.itemStatus.name列表中
for
(
let
i
of
this
.
itemStatus
.
name
){
// 从flexNameList中删除被选中的项,刷新UI
this
.
flexNameList
.
splice
(
this
.
flexNameList
.
indexOf
(
i
),
1
)
}
}
})
})
}.
alignItems
(
HorizontalAlign
.
Center
)
}
}
```
## 完整代码
本例完整代码如下:
```
ts
import
promptAction
from
'
@ohos.promptAction
'
;
@
Entry
@
Component
struct
CheckboxDemo
{
@
State
flexNameList
:
string
[]
=
[
'
苹果
'
,
'
菠萝
'
,
'
柚子
'
]
private
itemStatus
:
CheckboxGroupResult
@
Builder
flexItem
(
checkboxName
:
string
,
groupName
:
string
){
Flex
({
justifyContent
:
FlexAlign
.
Center
,
alignItems
:
ItemAlign
.
Center
})
{
Checkbox
({
name
:
checkboxName
,
group
:
groupName
})
.
selectedColor
(
'
#007DFF
'
)
Text
(
checkboxName
)
.
margin
({
right
:
20
})
.
fontSize
(
14
)
.
lineHeight
(
20
)
.
fontColor
(
'
#182431
'
)
.
fontWeight
(
500
)
}.
margin
({
left
:
36
})
}
build
()
{
Column
()
{
if
(
this
.
flexNameList
.
length
!=
0
){
Flex
({
justifyContent
:
FlexAlign
.
Center
,
alignItems
:
ItemAlign
.
Center
})
{
CheckboxGroup
({
group
:
'
fruit_list
'
})
.
selectedColor
(
'
#007DFF
'
)
.
onChange
((
itemName
:
CheckboxGroupResult
)
=>
{
this
.
itemStatus
=
itemName
})
Text
(
'
水果清单
'
)
.
margin
({
right
:
20
})
.
fontSize
(
14
)
.
lineHeight
(
20
)
.
fontColor
(
'
#182431
'
)
.
fontWeight
(
FontWeight
.
Bold
)
}.
margin
({
top
:
150
})
ForEach
(
this
.
flexNameList
,(
item
:
any
)
=>
{
this
.
flexItem
(
item
,
'
fruit_list
'
)
})
Button
(
"
删除
"
)
.
margin
({
top
:
20
,
left
:
35
})
.
fontSize
(
15
)
.
padding
({
top
:
5
,
bottom
:
5
,
left
:
15
,
right
:
15
})
.
onClick
(()
=>
{
promptAction
.
showDialog
({
title
:
''
,
message
:
'
确定删除吗?
'
,
buttons
:[
{
text
:
'
取消
'
,
color
:
'
#000000
'
},
{
text
:
'
确定
'
,
color
:
'
#000000
'
}
]
})
.
then
(
data
=>
{
if
(
data
.
index
===
1
){
for
(
let
i
of
this
.
itemStatus
.
name
){
this
.
flexNameList
.
splice
(
this
.
flexNameList
.
indexOf
(
i
),
1
)
}
}
})
})
}
else
{
}
}.
alignItems
(
HorizontalAlign
.
Center
)
}
}
```
## 参考
-
[
ChecckboxGroup
](
../application-dev/reference/arkui-ts/ts-basic-components-checkboxgroup.md
)
-
[
Checkbox
](
../application-dev/reference/arkui-ts/ts-basic-components-checkbox.md
)
-
[
Flex
](
../application-dev/reference/arkui-ts/ts-container-flex.md
)
-
[
Button
](
../application-dev/reference/arkui-ts/ts-basic-components-button.md
)
-
[
ohos.promptAction (弹窗)
](
../application-dev/reference/apis/js-apis-promptAction.md
)
-
[
ForEach循环渲染
](
../application-dev/quick-start/arkts-rendering-control.md
)
-
[
@State状态管理
](
../application-dev/quick-start/arkts-state-mgmt-page-level.md
)
-
[
@Builder动态构建UI元素
](
../application-dev/quick-start/arkts-dynamic-ui-elememt-building.md
)
\ No newline at end of file
zh-cn/third-party-cases/figures/Pixel-Convertion.gif
0 → 100644
浏览文件 @
adaba53c
77.1 KB
zh-cn/third-party-cases/figures/checkbox-after-improve.PNG
0 → 100644
浏览文件 @
adaba53c
13.9 KB
zh-cn/third-party-cases/figures/checkbox-before-improve.PNG
0 → 100644
浏览文件 @
adaba53c
15.0 KB
zh-cn/third-party-cases/figures/delete-checkboxitem.gif
0 → 100644
浏览文件 @
adaba53c
140.4 KB
zh-cn/third-party-cases/figures/preference-storage.gif
0 → 100644
浏览文件 @
adaba53c
353.7 KB
zh-cn/third-party-cases/image-format-transfer.md
0 → 100644
浏览文件 @
adaba53c
# 如何转换图片格式
## 场景说明
当我们获取到图片或者视频的缩略图后,返回的是pixelMap,此时有开发者会有疑问如何将pixelMap转换成jpeg等其他格式的图片,其实使用image类中的packing方法就可以将pixelMap重新打包成新的格式(当前只支持jpeg,webp格式),再使用文件管理就可以将图片存入到应用的沙箱路径。本例即为大家介绍如何完成图片格式转换。
## 运行环境
本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:
-
IDE: DevEco Studio 4.0 Beta1
-
SDK: Ohos_sdk_public 4.0.7.5 (API Version 10 Beta1)
## 效果呈现
本例最终实现效果为:将工程资源文件中png格式的图片转换为jpg格式,并保存在设备中。由于本例不涉及UI讲解,所以不在此提供UI效果。
## 实现思路
本例中完成图片格式转换包含三个关键步骤,相关步骤及实现方案如下:
-
获取到要转换图片的PixelMap数据:使用image的createPixelMap方法获取到图片的PixelMap数据。
-
将图片的PixelMap重新打包转换为其他格式:使用packing方法进行打包,打包时可以设置格式、压缩质量等。
-
将重新打包好的图片保存到应用目录:使用图库选择器photoViewPicker的相关功能以及file读写操作完成图片的保存。
## 开发步骤
由于本例重点讲解图片格式的转换,所以开发步骤会着重讲解相关实现,不相关的内容不做介绍,全量代码可参考完整代码章节。
1.
获取要转换图片的PixelMap数据。
先通过上下文context获取到资源管理器resourceManager,然后通过资源管理器获取到图片数据,然后获取图片的ArrayBuffer,最后通过ArrayBuffer创建imageSource,获取到pixelMap,完成图片解码。
具体代码如下:
```ts
import common from '@ohos.app.ability.common';
@Entry
@Component
struct Index {
...
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
...
async getPixelMap(){
// 获取resourceManager资源管理
const resourceMgr = this.context.resourceManager
// 获取rawfile文件夹下imagetransfer.PNG的ArrayBuffer
const fileData = await resourceMgr.getMediaContent($r('app.media.imagetransfer'))
const buffer = fileData.buffer
// 创建imageSource
const imageSource = image.createImageSource(buffer)
// 获取PixelMap
const pixelMap = await imageSource.createPixelMap()
return pixelMap
}
...
}
```
2.
将图片的PixelMap重新打包转换为其他格式。
先通过createImagePacker构建ImagePacker实例,再通过该实例调用packing方法进行打包,打包时传入获取到的PixelMap数据及重新打包的图片格式等相关配置信息。
具体代码如下:
```ts
...
@State src:PixelMap = undefined
...
// 页面加载前将获取到的图片PixelMap数据赋值给状态变量src
async aboutToAppear() {
this.src = await this.getPixelMap()
}
...
// 创建ImagePacker实例
let imagePackerApi = image.createImagePacker();
let options = {
// 设置重新打包的图片格式
format: 'image/jpeg',
quality: 98
};
// 打包时传入图片的PixelMap:src和图片打包选项:option,异步获取打包后的数据data
imagePackerApi.packing(this.src, options).then((data) => {
console.log('Succeeded in packing the image.');
}).catch(error => {
console.log('Failed to pack the image..');
....
})
```
3.
将重新打包好的图片保存到应用目录。
使用图库选择器photoViewPicker保存文件,保存时可以在保存界面选择保存路径并设定文件名。此时保存的是空文件,然后再使用file将重新打包的图片数据写入保存的文件中,保存完成后我们便可以在保存路径下找到转换格式后的图片文件了。
具体代码如下:
```ts
...
// 打包时传入图片的pixelmap:src和图片打包选项:option,异步获取打包后的数据data
imagePackerApi.packing(this.src, options).then((data) => {
// 创建文件管理器保存选项实例
let photoSaveOptions = new picker.PhotoSaveOptions();
// 保存文件名(可选)
photoSaveOptions.newFileNames = ["imageTransfer.jpg"];
let photoViewPicker = new picker.PhotoViewPicker();
// 保存时传入保存的文件名:photoSaveOptions
photoViewPicker.save(photoSaveOptions)
.then((photoSaveResult) => {
setTimeout(() => {
// 获取到保存文件的URI,后续进行文件读取等操作
this.uri = photoSaveResult[0];
fs.open(this.uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE).then((file) => {
// 将图片打包数据data写入保存的文件
fs.write(file.fd, data).then((number) => {
console.info("foo imagetest: write data to file succeed and size is:" + number);
}).catch((err) => {
console.info("foo imagetest: write data to file failed with error:" + err);
});
// 完成文件写入后,关闭文件
fs.close(file, (err) => {
if (err) {
console.info("close file failed with error message: " + err.message + ", error code: " + err.code);
} else {
console.info("close file success");
}
});
}).catch((err) => {
console.info("foo open file failed with error message: " + err.message + ", error code: " + err.code);
});
}, 200)
})
.catch((err) => {
console.error('PhotoViewPicker.save failed with err: ' + err);
})
})
...
```
## 完整代码
本例完整代码如下:
```
ts
import
image
from
'
@ohos.multimedia.image
'
;
import
fs
from
'
@ohos.file.fs
'
;
import
common
from
'
@ohos.app.ability.common
'
;
import
picker
from
'
@ohos.file.picker
'
;
@
Entry
@
Component
struct
Index
{
@
State
src
:
PixelMap
=
undefined
context
:
common
.
UIAbilityContext
=
getContext
(
this
)
as
common
.
UIAbilityContext
private
uri
=
null
// 页面加载前将获取到的图片PixelMap数据赋值给状态变量src
async
aboutToAppear
()
{
this
.
src
=
await
this
.
getPixelMap
()
}
async
getPixelMap
(){
// 获取resourceManager资源管理
const
resourceMgr
=
this
.
context
.
resourceManager
// 获取rawfile文件夹下httpimage.PNG的ArrayBuffer
const
fileData
=
await
resourceMgr
.
getMediaContent
(
$r
(
'
app.media.contact6
'
))
const
buffer
=
fileData
.
buffer
// 创建imageSource
const
imageSource
=
image
.
createImageSource
(
buffer
)
// 创建PixelMap
const
pixelMap
=
await
imageSource
.
createPixelMap
()
return
pixelMap
console
.
log
(
'
pixelMap
'
+
JSON
.
stringify
(
this
.
src
.
getPixelBytesNumber
()))
}
build
()
{
Row
()
{
Column
()
{
Button
(
'
转换图片格式:png->jpeg
'
)
.
onClick
(()
=>
{
// 创建ImagePacker实例
let
imagePackerApi
=
image
.
createImagePacker
();
// 设置重新打包的图片格式,及图片压缩质量
let
options
=
{
format
:
'
image/jpeg
'
,
quality
:
98
};
// 打包时传入图片的pixelmap:src和图片打包选项:option,异步获取打包后的数据data
imagePackerApi
.
packing
(
this
.
src
,
options
).
then
((
data
)
=>
{
// 创建文件管理器保存选项实例
let
photoSaveOptions
=
new
picker
.
PhotoSaveOptions
();
// 保存文件名(可选)
photoSaveOptions
.
newFileNames
=
[
"
imageTransfer.jpg
"
];
let
photoViewPicker
=
new
picker
.
PhotoViewPicker
();
// 保存时传入保存的文件名:photoSaveOptions
photoViewPicker
.
save
(
photoSaveOptions
)
.
then
((
photoSaveResult
)
=>
{
console
.
log
(
'
foo start
'
)
setTimeout
(()
=>
{
// 获取到图片的URI后进行文件读取等操作
this
.
uri
=
photoSaveResult
[
0
];
fs
.
open
(
this
.
uri
,
fs
.
OpenMode
.
READ_WRITE
|
fs
.
OpenMode
.
CREATE
).
then
((
file
)
=>
{
// 将图片打包数据data写入保存的文件
fs
.
write
(
file
.
fd
,
data
).
then
((
number
)
=>
{
console
.
info
(
"
foo imagetest: write data to file succeed and size is:
"
+
number
);
}).
catch
((
err
)
=>
{
console
.
info
(
"
foo imagetest: write data to file failed with error:
"
+
err
);
});
// 完成文件写入后,关闭文件
fs
.
close
(
file
,
(
err
)
=>
{
if
(
err
)
{
console
.
info
(
"
close file failed with error message:
"
+
err
.
message
+
"
, error code:
"
+
err
.
code
);
}
else
{
console
.
info
(
"
close file success
"
);
}
});
}).
catch
((
err
)
=>
{
console
.
info
(
"
foo open file failed with error message:
"
+
err
.
message
+
"
, error code:
"
+
err
.
code
);
});
},
200
)
})
.
catch
((
err
)
=>
{
console
.
error
(
'
PhotoViewPicker.save failed with err:
'
+
err
);
})
})
})
}
.
width
(
'
100%
'
)
}
.
height
(
'
100%
'
)
}
}
```
## 参考
-
[
@ohos.multimedia.image (图片处理)
](
../application-dev/reference/apis/js-apis-image.md
)
-
[
@ohos.file.fs (文件管理)
](
../application-dev/reference/apis/js-apis-file-fs.md
)
-
[
@ohos.file.picker (选择器)
](
../application-dev/reference/apis/js-apis-file-picker.md
)
\ No newline at end of file
zh-cn/third-party-cases/pixel-format-transfer.md
0 → 100644
浏览文件 @
adaba53c
# 像素转换(ArkTs)
在日常应用页面布局设计时,开发者需要知道每个组件的样式及位置,这时就需要了解下长度单位及相互转换方法,ArkUI 开发框架提供了4种像素单位供开发者使用,分别是: px 、 vp 、 fp 和 lpx ,框架采用 vp 为基准数据单位,本篇就简单为大家介绍下像素单位的基本知识与像素单位转换API的使用。通过像素转换案例,向开发者讲解了如何使用像素单位设置组件的尺寸、字体的大小以及不同像素单位之间的转换方法。
## 效果呈现
本例最终效果如下:

## 运行环境
本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:
-
IDE: DevEco Studio 3.1 Release
-
SDK: Ohos_sdk_public 3.2.12.5(API Version 9 Release)
## 实现思路
本篇案例主要功能包括:①像素单位基本知识介绍;②像素单位转换相关API的使用。
*
构建入口页面:该页面包含两个button组件,通过点击按钮事件,实现到详细页面的跳转
*
像素单位介绍页面:
*
构建IntroducitonViewModel.ets
创建自定义接口IntroductionItem,根据IntroductionItem接口参数,创建对象数组INTRODUCE_LIST,向对象数组INTRODUCE_LIST中填充像素单位介绍页面所需参数内容。
*
构建IntroducitonPage.ets
通过ForEach循环渲染上一步骤中对象数组中的每个Item;通过if判断组件的显隐,同时添加样式,完成像素介绍页面。
*
像素转换页面:
*
构建ConvertionViewModel.ets
创建自定义接口ConversionItem,根据ConversionItem接口参数,创建对象数组ConversionViewModel,向对象数组ConversionViewModel中填充像素转换页面所需参数内容。
*
构建ConvertionPage.ets
通过ForEach循环渲染上一步构建的ConversionViewModel的每个子菜单Item,同时添加样式,构建像素介绍页面。
## 开发步骤
1.
构建入口页面:该页面包含两个button按钮,通过点击按钮事件,实现到详细页的跳转。
具体代码如下:
```
ts
// entry/src/main/ets/pages/IndexPage.ets
import
router
from
'
@ohos.router
'
;
@
Entry
@
Component
struct
IndexPage
{
// 定义jumpPage方法,实现路由跳转
jumpPage
(
url
:
string
)
{
router
.
pushUrl
({
url
})
}
build
()
{
Column
({
space
:
24
})
{
Button
(
'
像素介绍
'
)
.
height
(
'
40vp
'
)
.
width
(
'
100%
'
)
.
backgroundColor
(
$r
(
'
app.color.blue_background
'
))
// 点击时回调jumpPage方法,跳转到pages/IntroductionPage页面
.
onClick
(()
=>
this
.
jumpPage
(
'
pages/IntroductionPage
'
))
Button
(
'
像素转换
'
)
.
height
(
'
40vp
'
)
.
width
(
'
100%
'
)
.
backgroundColor
(
$r
(
'
app.color.blue_background
'
))
// 点击时回调jumpPage方法,跳转到pages/ConversionPage页面
.
onClick
(()
=>
this
.
jumpPage
(
'
pages/ConversionPage
'
))
}
.
backgroundColor
(
$r
(
'
app.color.page_background
'
))
.
justifyContent
(
FlexAlign
.
Center
)
.
padding
(
24
)
.
width
(
'
100%
'
)
.
height
(
'
100%
'
)
}
}
```
2.
像素单位介绍页面创建。
此页面主要系统介绍像素单位的概念,包含px、vp、lpx以及fp,并在页面中 为Text组件的宽度属性设置不同的像素单位(如px、vp、lpx),fp像素单位则设置为Text组件的字体大小。
* 从效果图看,此页面由4个功能相同的菜单组成,我们先构建功能菜单。
创建IntroducitonViewModel.ets定义每个子功能菜单Item。
具体代码如下:
```ts
// entry/src/main/ets/viewmodel/IntroducitonViewModel.ets
// 创建自定义接口,定义每个Item中的内容
interface IntroductionItem {
name: string;
title: string;
subTitle: string;
value: string;
smallFontSize: number;
largeFontSize: number;
}
// 根据自定义接口IntroductionItem,填充内容
const INTRODUCE_LIST: IntroductionItem[] = [
{
name: 'px',
title: '屏幕物理像素单位。',
subTitle: null,
value: '200px',
smallFontSize: 0,
largeFontSize: 0
},
{
name: 'vp',
title:'屏幕密度相关像素,根据屏幕像素密度转换为屏幕物理像素。',
value:'200vp',
subTitle:'像素密度为160dpi的设备上1vp=1px,1vp对应的物理屏幕像素=(屏幕像素密度/160)px',
smallFontSize: 0,
largeFontSize: 0
},
{
name: 'lpx',
title:'视窗逻辑像素单位,lpx单位为实际屏幕宽度与逻辑宽度(通过designWidth配置)的比值。',
subTitle: null,
value: '200lpx',
smallFontSize: 0,
largeFontSize: 0
},
{
name: 'fp',
title:'字体像素,与vp类似,随系统字体大小设置变化。',
subTitle:'默认情况下与vp相同,即1vp=1fp,如果用户手动调整了系统字体,scale为缩放比例,设置后的字体大小(单位fp) = 设置前的字体大小 * scale',
value: '',
smallFontSize: 14,
largeFontSize: 24
}
];
// 定义类IntroductionViewModel,获取像素介绍页面的数据
class IntroductionViewModel {
getIntroductionList() {
let introductionItems = INTRODUCE_LIST;
return introductionItems;
}
}
let introductionViewModel = new IntroductionViewModel();
export default introductionViewModel as IntroductionViewModel;
```
* 渲染像素单位介绍页面,通过ForEach循环渲染上一步构建的IntroductionViewModel的每个子菜单Item;通过if判断组件的显隐,为显示的组件,添加样式,构建像素介绍页面。
具体代码如下:
```ts
// entry/src/main/ets/pages/IntroducitonPages.ets
import IntroductionViewModel from '../viewmodel/IntroductionViewModel';
interface IntroductionItem {
name: string;
title: Resource;
subTitle: Resource;
value: string;
smallFontSize: number;
largeFontSize: number;
}
@Extend(Text) function titleTextStyle() {
.fontColor($r('app.color.title_font'))
.fontFamily('HarmonyHeiTi_Medium')
.fontWeight(500)
}
@Entry
@Component
struct IntroductionPage {
build() {
Column() {
Navigation() {
List({ space: 12 }) {
//通过ForEach循环渲染Item,构建像素介绍页面
ForEach(IntroductionViewModel.getIntroductionList(), (item: IntroductionItem) => {
//渲染每个Item
ListItem() {
Column() {
Text(item.name)
.titleTextStyle()
.fontSize('16fp')
Text(item.title)
.titleTextStyle()
.fontSize('14fp')
.fontFamily('HarmonyHeiTi')
.lineHeight('20fp')
.margin({ top: '8vp'})
.fontWeight(400)
// subTitle非空,添加Text组件,显示subTitle内容,同时添加样式;不存在则不显示
if (item.subTitle) {
Text(item.subTitle)
.titleTextStyle()
.opacity(0.6)
.lineHeight('16fp')
.fontSize('12fp')
.fontFamily($r('app.string.HarmonyHeiTi'))
.margin({ top: '20vp' })
.fontWeight(400)
}
// value非空,添加Text组件且通过宽度属性设置不同的像素单位
if (item.value.length > 0) {
Text(item.value)
.titleTextStyle()
.fontColor($r('app.color.item_background'))
.fontSize('16fp')
.textAlign(TextAlign.Center)
.backgroundColor($r('app.color.blue_background'))
.height('28vp')
.width(item.value)
.borderRadius('4vp')
.margin({ top: '12vp' })
// value为空,添加两个text组件,使用fp像素单位设置为Text组件的字体大小
} else {
Column() {
Text($r('app.string.font_desc', item.smallFontSize))
.titleTextStyle()
.fontSize(item.smallFontSize)
Text($r('app.string.font_desc', item.largeFontSize))
.titleTextStyle()
.fontSize(item.largeFontSize)
.margin({ top: '6vp' })
}
.alignItems(HorizontalAlign.Start)
.backgroundColor($r('app.color.font_background'))
.width('100%')
.borderRadius('12vp')
.padding('12vp')
.margin({ top: '12vp' })
}
}
.alignItems(HorizontalAlign.Start)
.width('100%')
.padding('12vp')
.borderRadius('24vp')
.backgroundColor('#FFFFFF')
}
.padding({
left: '12vp',
right: '12vp'
})
})
}
.width('100%')
.height('100%')
}
.titleMode(NavigationTitleMode.Mini)
.title('像素介绍')
}
.backgroundColor($r('app.color.page_background'))
.width('100%')
.height('100%')
}
}
```
3.
像素转换页面创建。
此页面主要是通过使用像素转换API,实现不同像素单位之间的相互转换功能。
* 从效果图看,此页面由3个功能相同的菜单组成,我们先构建功能菜单。
创建ConversionViewModel.ets定义每个子功能菜单Item。
具体代码如下:
```ts
// entry/src/main/ets/viewmodel/ConversionViewModel.ets
// 创建自定义接口,定义每个Item中的内容
interface ConversionItem {
title: string;
subTitle: string;
value: number;
conversionTitle: string;
conversionSubTitle: string;
conversionValue: number;
notice: string;
}
// 定义类ConversionViewModel,获取像素转换页面的数据
class ConversionViewModel {
getConversionList() {
let conversionItems = CONVERSION_LIST;
return conversionItems;
}
}
// 根据自定义接口ConversionItem,填充内容
export const CONVERSION_LIST: ConversionItem[] = [
{
title: 'vp > px',
subTitle: `vp2px(60)`,
value: vp2px(60),
conversionTitle: 'px > vp',
conversionSubTitle: `px2vp(60)`,
conversionValue: px2vp(60),
notice: null
},
{
title: 'fp > px',
subTitle: `fp2px(60)`,
value: fp2px(60),
conversionTitle: 'px > fp',
conversionSubTitle: `px2fp(60})`,
conversionValue: px2fp(60),
notice: null
},
{
title: 'lpx > px',
subTitle: `lpx2px(60)`,
value: lpx2px(60),
conversionTitle: 'px > lpx',
conversionSubTitle: `px2lpx(60)`,
conversionValue: px2lpx(60),
notice: 'lpx与px之间的转换,需要根据实际情况设置designWidth'
}
];
let conversionViewModel = new ConversionViewModel();
export default conversionViewModel as ConversionViewModel;
```
* 渲染像素单位介绍页面,通过ForEach循环渲染上一步构建的ConversionViewModel的每个子菜单Item,同时添加样式,构建像素介绍页面。
具体代码如下:
```ts
// entry/src/main/ets/pages/ConversionPage.ets
import ConversionViewModel from '../viewmodel/ConversionViewModel';
interface ConversionItem {
title: string;
subTitle: string;
value: number;
conversionTitle: string;
conversionSubTitle: string;
conversionValue: number;
notice: string;
}
@Extend(Text) function descTextStyle() {
.fontColor($r('app.color.title_font'))
.fontSize('14fp')
.fontFamily($r('app.string.HarmonyHeiTi'))
.lineHeight('20fp')
.fontWeight(400)
.margin({ top: '8vp' })
}
@Extend(Text) function titleTextStyle() {
.fontColor($r('app.color.title_font'))
.fontSize('16fp')
.fontFamily($r('app.string.HarmonyHeiTi_Medium'))
.fontWeight(500)
}
@Styles function blueStyle() {
.backgroundColor($r('app.color.blue_background'))
.height('28vp')
.borderRadius('4vp')
.margin({ top: '4vp' })
}
@Entry
@Component
struct ConversionPage {
build() {
Column() {
Navigation() {
List({ space: 12 }) {
//通过ForEach循环渲染Item,构建像素转换页面
ForEach(ConversionViewModel.getConversionList(), (item: ConversionItem) => {
//渲染每个Item
ListItem() {
Column() {
Text(item.title)
.titleTextStyle()
.margin({ top: '6vp' })
Text(item.subTitle)
.descTextStyle()
.opacity(0.6)
Row()
.blueStyle()
// 为宽度属性设置不同的像素单位
.width(item.value)
Text(item.conversionTitle)
.titleTextStyle()
.margin({ top: '18vp' })
Text(item.conversionSubTitle)
.descTextStyle()
.opacity(0.6)
Row()
.blueStyle()
// 为宽度属性设置不同的像素单位
.width(item.conversionValue)
if (item.notice) {
Text(item.notice)
.descTextStyle()
.fontColor($r('app.color.notice_font'))
}
}
.alignItems(HorizontalAlign.Start)
.width('100%')
.padding('12vp')
.borderRadius('24vp')
.backgroundColor('#FFFFFF')
}
.padding({left: '12vp',right: '12vp'})
})
}
.width('100%')
.height('100%')
}
.titleMode(NavigationTitleMode.Mini)
.title('像素转换')
}
.backgroundColor($r('app.color.page_background'))
.width('100%')
.height('100%')
}
}
```
## 完整代码
本例完整代码如下:
*
应用主页面:entry/src/main/ets/pages/IndexPage.ets。
```
ts
import
router
from
'
@ohos.router
'
;
@
Entry
@
Component
struct
IndexPage
{
// 定义jumpPage方法,实现路由跳转
jumpPage
(
url
:
string
)
{
router
.
pushUrl
({
url
})
}
build
()
{
Column
({
space
:
24
})
{
Button
(
'
像素介绍
'
)
.
height
(
'
40vp
'
)
.
width
(
'
100%
'
)
.
backgroundColor
(
$r
(
'
app.color.blue_background
'
))
// 点击时回调jumpPage方法,跳转到pages/IntroductionPage页面
.
onClick
(()
=>
this
.
jumpPage
(
'
pages/IntroductionPage
'
))
Button
(
'
像素转换
'
)
.
height
(
'
40vp
'
)
.
width
(
'
100%
'
)
.
backgroundColor
(
$r
(
'
app.color.blue_background
'
))
// 点击时回调jumpPage方法,跳转到pages/ConversionPage页面
.
onClick
(()
=>
this
.
jumpPage
(
'
pages/ConversionPage
'
))
}
.
backgroundColor
(
$r
(
'
app.color.page_background
'
))
.
justifyContent
(
FlexAlign
.
Center
)
.
padding
(
24
)
.
width
(
'
100%
'
)
.
height
(
'
100%
'
)
}
}
```
*
像素介绍ViewModel:entry/src/main/ets/viewmodel/IntroducitonViewModel.ets。
```
ts
// 创建自定义接口,定义每个Item中的内容
interface
IntroductionItem
{
name
:
string
;
title
:
string
;
subTitle
:
string
;
value
:
string
;
smallFontSize
:
number
;
largeFontSize
:
number
;
}
// 根据自定义接口IntroductionItem,填充内容
const
INTRODUCE_LIST
:
IntroductionItem
[]
=
[
{
name
:
'
px
'
,
title
:
'
屏幕物理像素单位。
'
,
subTitle
:
null
,
value
:
Constants
.
PIXEL_WIDTH
+
'
px
'
,
smallFontSize
:
0
,
largeFontSize
:
0
},
{
name
:
'
vp
'
,
title
:
'
屏幕密度相关像素,根据屏幕像素密度转换为屏幕物理像素。
'
,
value
:
Constants
.
PIXEL_WIDTH
+
'
vp
'
,
subTitle
:
'
像素密度为160dpi的设备上1vp=1px,1vp对应的物理屏幕像素=(屏幕像素密度/160)px
'
,
smallFontSize
:
0
,
largeFontSize
:
0
},
{
name
:
'
lpx
'
,
title
:
'
视窗逻辑像素单位,lpx单位为实际屏幕宽度与逻辑宽度(通过designWidth配置)的比值。
'
,
subTitle
:
null
,
value
:
Constants
.
PIXEL_WIDTH
+
'
lpx
'
,
smallFontSize
:
0
,
largeFontSize
:
0
},
{
name
:
'
fp
'
,
title
:
'
字体像素,与vp类似,随系统字体大小设置变化。
'
,
subTitle
:
'
默认情况下与vp相同,即1vp=1fp,如果用户手动调整了系统字体,scale为缩放比例,设置后的字体大小(单位fp) = 设置前的字体大小 * scale
'
,
value
:
''
,
smallFontSize
:
Constants
.
SMALL_FONT_SIZE
,
largeFontSize
:
Constants
.
LARGE_FONT_SIZE
}
];
// 定义类IntroductionViewModel,获取像素介绍页面的数据
class
IntroductionViewModel
{
getIntroductionList
()
{
let
introductionItems
=
INTRODUCE_LIST
;
return
introductionItems
;
}
}
let
introductionViewModel
=
new
IntroductionViewModel
();
export
default
introductionViewModel
as
IntroductionViewModel
;
```
*
像素介绍页面:entry/src/main/ets/pages/IntroducitonPages.ets。
```
ts
import
IntroductionViewModel
from
'
../viewmodel/IntroductionViewModel
'
;
interface
IntroductionItem
{
name
:
string
;
title
:
Resource
;
subTitle
:
Resource
;
value
:
string
;
smallFontSize
:
number
;
largeFontSize
:
number
;
}
@
Extend
(
Text
)
function
titleTextStyle
()
{
.
fontColor
(
$r
(
'
app.color.title_font
'
))
.
fontFamily
(
'
HarmonyHeiTi_Medium
'
)
.
fontWeight
(
500
)
}
@
Entry
@
Component
struct
IntroductionPage
{
build
()
{
Column
()
{
Navigation
()
{
List
({
space
:
12
})
{
//通过ForEach循环渲染Item,构建像素介绍页面
ForEach
(
IntroductionViewModel
.
getIntroductionList
(),
(
item
:
IntroductionItem
)
=>
{
//渲染每个Item
ListItem
()
{
Column
()
{
Text
(
item
.
name
)
.
titleTextStyle
()
.
fontSize
(
'
16fp
'
)
Text
(
item
.
title
)
.
titleTextStyle
()
.
fontSize
(
'
14fp
'
)
.
fontFamily
(
'
HarmonyHeiTi
'
)
.
lineHeight
(
'
20fp
'
)
.
margin
({
top
:
'
8vp
'
})
.
fontWeight
(
400
)
// subTitle非空,添加Text组件,显示subTitle内容,同时添加样式;不存在则不显示
if
(
item
.
subTitle
)
{
Text
(
item
.
subTitle
)
.
titleTextStyle
()
.
opacity
(
0.6
)
.
lineHeight
(
'
16fp
'
)
.
fontSize
(
'
12fp
'
)
.
fontFamily
(
$r
(
'
app.string.HarmonyHeiTi
'
))
.
margin
({
top
:
'
20vp
'
})
.
fontWeight
(
400
)
}
// value非空,添加Text组件且通过宽度属性设置不同的像素单位
if
(
item
.
value
.
length
>
0
)
{
Text
(
item
.
value
)
.
titleTextStyle
()
.
fontColor
(
$r
(
'
app.color.item_background
'
))
.
fontSize
(
'
16fp
'
)
.
textAlign
(
TextAlign
.
Center
)
.
backgroundColor
(
$r
(
'
app.color.blue_background
'
))
.
height
(
'
28vp
'
)
.
width
(
item
.
value
)
.
borderRadius
(
'
4vp
'
)
.
margin
({
top
:
'
12vp
'
})
// value为空,添加两个text组件,使用fp像素单位设置为Text组件的字体大小
}
else
{
Column
()
{
Text
(
$r
(
'
app.string.font_desc
'
,
item
.
smallFontSize
))
.
titleTextStyle
()
.
fontSize
(
item
.
smallFontSize
)
Text
(
$r
(
'
app.string.font_desc
'
,
item
.
largeFontSize
))
.
titleTextStyle
()
.
fontSize
(
item
.
largeFontSize
)
.
margin
({
top
:
'
6vp
'
})
}
.
alignItems
(
HorizontalAlign
.
Start
)
.
backgroundColor
(
$r
(
'
app.color.font_background
'
))
.
width
(
'
100%
'
)
.
borderRadius
(
'
12vp
'
)
.
padding
(
'
12vp
'
)
.
margin
({
top
:
'
12vp
'
})
}
}
.
alignItems
(
HorizontalAlign
.
Start
)
.
width
(
'
100%
'
)
.
padding
(
'
12vp
'
)
.
borderRadius
(
'
24vp
'
)
.
backgroundColor
(
'
#FFFFFF
'
)
}
.
padding
({
left
:
'
12vp
'
,
right
:
'
12vp
'
})
})
}
.
width
(
'
100%
'
)
.
height
(
'
100%
'
)
}
.
titleMode
(
NavigationTitleMode
.
Mini
)
.
title
(
'
像素介绍
'
)
}
.
backgroundColor
(
$r
(
'
app.color.page_background
'
))
.
width
(
'
100%
'
)
.
height
(
'
100%
'
)
}
}
```
*
像素转换ViewModel:entry/src/main/ets/viewmodel/ConversionViewModel.ets。
```
ts
// 创建自定义接口,定义每个Item中的内容
interface
ConversionItem
{
title
:
string
;
subTitle
:
string
;
value
:
number
;
conversionTitle
:
string
;
conversionSubTitle
:
string
;
conversionValue
:
number
;
notice
:
string
;
}
// 定义类ConversionViewModel,获取像素转换页面的数据
class
ConversionViewModel
{
getConversionList
()
{
let
conversionItems
=
CONVERSION_LIST
;
return
conversionItems
;
}
}
// 根据自定义接口ConversionItem,填充内容
export
const
CONVERSION_LIST
:
ConversionItem
[]
=
[
{
title
:
'
vp > px
'
,
subTitle
:
`vp2px(60)`
,
value
:
vp2px
(
60
),
conversionTitle
:
'
px > vp
'
,
conversionSubTitle
:
`px2vp(60)`
,
conversionValue
:
px2vp
(
60
),
notice
:
null
},
{
title
:
'
fp > px
'
,
subTitle
:
`fp2px(60)`
,
value
:
fp2px
(
60
),
conversionTitle
:
'
px > fp
'
,
conversionSubTitle
:
`px2fp(60})`
,
conversionValue
:
px2fp
(
60
),
notice
:
null
},
{
title
:
'
lpx > px
'
,
subTitle
:
`lpx2px(60)`
,
value
:
lpx2px
(
60
),
conversionTitle
:
'
px > lpx
'
,
conversionSubTitle
:
`px2lpx(60)`
,
conversionValue
:
px2lpx
(
60
),
notice
:
'
lpx与px之间的转换,需要根据实际情况设置designWidth
'
}
];
let
conversionViewModel
=
new
ConversionViewModel
();
export
default
conversionViewModel
as
ConversionViewModel
;
```
*
像素转换页面:entry/src/main/ets/pages/ConversionPage.ets。
```
ts
import
ConversionViewModel
from
'
../viewmodel/ConversionViewModel
'
;
interface
ConversionItem
{
title
:
string
;
subTitle
:
string
;
value
:
number
;
conversionTitle
:
string
;
conversionSubTitle
:
string
;
conversionValue
:
number
;
notice
:
string
;
}
@
Extend
(
Text
)
function
descTextStyle
()
{
.
fontColor
(
$r
(
'
app.color.title_font
'
))
.
fontSize
(
'
14fp
'
)
.
fontFamily
(
$r
(
'
app.string.HarmonyHeiTi
'
))
.
lineHeight
(
'
20fp
'
)
.
fontWeight
(
400
)
.
margin
({
top
:
'
8vp
'
})
}
@
Extend
(
Text
)
function
titleTextStyle
()
{
.
fontColor
(
$r
(
'
app.color.title_font
'
))
.
fontSize
(
'
16fp
'
)
.
fontFamily
(
$r
(
'
app.string.HarmonyHeiTi_Medium
'
))
.
fontWeight
(
500
)
}
@
Styles
function
blueStyle
()
{
.
backgroundColor
(
$r
(
'
app.color.blue_background
'
))
.
height
(
'
28vp
'
)
.
borderRadius
(
'
4vp
'
)
.
margin
({
top
:
'
4vp
'
})
}
@
Entry
@
Component
struct
ConversionPage
{
build
()
{
Column
()
{
Navigation
()
{
List
({
space
:
12
})
{
//通过ForEach循环渲染Item,构建像素转换页面
ForEach
(
ConversionViewModel
.
getConversionList
(),
(
item
:
ConversionItem
)
=>
{
//渲染每个Item
ListItem
()
{
Column
()
{
Text
(
item
.
title
)
.
titleTextStyle
()
.
margin
({
top
:
'
6vp
'
})
Text
(
item
.
subTitle
)
.
descTextStyle
()
.
opacity
(
0.6
)
Row
()
.
blueStyle
()
// 为宽度属性设置不同的像素单位
.
width
(
item
.
value
)
Text
(
item
.
conversionTitle
)
.
titleTextStyle
()
.
margin
({
top
:
'
18vp
'
})
Text
(
item
.
conversionSubTitle
)
.
descTextStyle
()
.
opacity
(
0.6
)
Row
()
.
blueStyle
()
// 为宽度属性设置不同的像素单位
.
width
(
item
.
conversionValue
)
if
(
item
.
notice
)
{
Text
(
item
.
notice
)
.
descTextStyle
()
.
fontColor
(
$r
(
'
app.color.notice_font
'
))
}
}
.
alignItems
(
HorizontalAlign
.
Start
)
.
width
(
'
100%
'
)
.
padding
(
'
12vp
'
)
.
borderRadius
(
'
24vp
'
)
.
backgroundColor
(
'
#FFFFFF
'
)
}
.
padding
({
left
:
'
12vp
'
,
right
:
'
12vp
'
})
})
}
.
width
(
'
100%
'
)
.
height
(
'
100%
'
)
}
.
titleMode
(
NavigationTitleMode
.
Mini
)
.
title
(
'
像素转换
'
)
}
.
backgroundColor
(
$r
(
'
app.color.page_background
'
))
.
width
(
'
100%
'
)
.
height
(
'
100%
'
)
}
}
```
## 参考
[
像素单位
](
../application-dev/reference/arkui-ts/ts-pixel-units.md
)
[
List
](
../application-dev/reference/arkui-ts/ts-container-list.md
)
[
Column
](
../application-dev/reference/arkui-ts/ts-container-column.md
)
[
Text
](
../application-dev/reference/arkui-ts/ts-basic-components-text.md
)
[
Navigation
](
../application-dev/reference/arkui-ts/ts-basic-components-navigation.md
)
\ No newline at end of file
zh-cn/third-party-cases/preferences-data-process.md
0 → 100644
浏览文件 @
adaba53c
# 首选项的使用
## 场景说明
用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。当用户希望有一个全局唯一存储的地方,可以采用用户首选项来进行存储。Preferences会将该数据缓存在内存中,当用户读取的时候,能够快速从内存中获取数据。Preferences会随着存放的数据量越多而导致应用占用的内存越大,因此,Preferences不适合存放过多的数据,适用的场景一般为应用保存用户的个性化设置(屏幕亮度,是否开启夜间模式)等。
本例以一个小示例为大家介绍如何使用用户首选项对数据进行存储、获取、删除。
## 效果呈现
本例最终效果如下:

## 运行环境
本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:
-
IDE: DevEco Studio 4.0 Beta1
-
SDK: Ohos_sdk_public 4.0.7.5 (API Version 10 Beta1)
## 实现思路
本例以设置屏幕亮度为例演示如何使用用户首选项管理数据,主要特性及实现方式如下:
-
当用户在文本框输入数据后,点击保存数据,用户首选项将数据缓存在内存中:通过dataPreferences类的getPreferences方法获取用户首选项实例,然后通过该实例调用put方法将数据写入内存。
-
当用户点击读取数据时,用户首选项将数据从内存中读取出来并显示在输入框中:通过用户首选项实例调用get方法获取到保存的数据,显示在输入框中。
-
当用户点击删除数据时,用户首选项将数据从内存中删除,用户无法继续读取数据:通过用户首选项实例调用delete方法删除保存的数据。
>  **说明:**
> 用户首选项的使用需要注意以下几点:
> - Key键为string类型,要求非空且长度不超过80个字节。
> - 如果Value值为string类型,请使用UTF-8编码格式,可以为空,不为空时长度不超过8192个字节。
> - 内存会随着存储数据量的增大而增大,所以存储的数据量应该是轻量级的,建议存储的数据不超过一万条,否则会在内存方面产生较大的开销。
## 开发步骤
由于本例重点讲解用户首选项的数据管理操作,所以开发步骤会着重讲解如何通过用户首选项完成数据的存储、读取和删除,全量代码可参考完整代码章节。
1.
首先自定义一个用户首选项类,根据业务封装相关方法方便后续调用。
其中包含数据处理的方法,用于完成数据的存储、读取和删除操作。用户首选项接口的使用方式主要在这部分呈现,需要重点关注。
具体代码如下:
```
ts
import
dataPreferences
from
'
@ohos.data.preferences
'
;
import
promptAction
from
'
@ohos.promptAction
'
;
import
ScreenBrightness
from
'
../common/bean/Brightness
'
;
let
context
=
getContext
(
this
);
let
preference
:
dataPreferences
.
Preferences
=
null
;
// 自定义用户首选项类
class
PreferenceModel
{
private
brightness
:
ScreenBrightness
// 创建用户首选项实例preference
async
getPreferencesFromStorage
()
{
try
{
preference
=
await
dataPreferences
.
getPreferences
(
context
,
'
setting.db
'
);
}
catch
(
err
)
{
Logger
.
error
(
'
[PreferenceModel]
'
,
`Failed to get preferences, Cause:
${
err
}
`
);
}
}
// 删除数据,调用dataPreferences的deletePreferences接口
async
deletePreferences
()
{
try
{
await
dataPreferences
.
deletePreferences
(
context
,
'
setting.db
'
);
}
catch
(
err
)
{
Logger
.
error
(
'
[PreferenceModel]
'
,
`Failed to delete preferences, Cause:
${
err
}
`
);
};
preference
=
null
;
this
.
showToastMessage
(
$r
(
'
app.string.delete_success_msg
'
));
}
// 保存数据
async
putPreference
(
screenBrightness
:
ScreenBrightness
)
{
if
(
preference
===
null
)
{
await
this
.
getPreferencesFromStorage
();
}
// 将用户输入的亮度数据保存到preference,调用用户首选项实例的put接口
try
{
await
preference
.
put
(
'
screenBrightness
'
,
JSON
.
stringify
(
screenBrightness
));
}
catch
(
err
)
{
Logger
.
error
(
'
[PreferenceModel]
'
,
`Failed to put value, Cause:
${
err
}
`
);
}
// 使用flush方法将preferences实例的数据存储到持久化文件,调用用户首选项实例的flush接口
await
preference
.
flush
();
}
// 获取数据,调用用户首选项实例的get接口
async
getPreference
()
{
let
screenBrightness
=
''
;
if
(
preference
===
null
)
{
await
this
.
getPreferencesFromStorage
();
}
try
{
screenBrightness
=
<
string
>
await
preference
.
get
(
'
screenBrightness
'
,
''
);
}
catch
(
err
)
{
Logger
.
error
(
'
[PreferenceModel]
'
,
`Failed to get value, Cause:
${
err
}
`
);
}
// 如果判断数据为空则提示用户先输入数据
if
(
screenBrightness
===
''
)
{
this
.
showToastMessage
(
$r
(
'
app.string.data_is_null_msg
'
));
return
;
}
this
.
showToastMessage
(
$r
(
'
app.string.read_success_msg
'
));
return
JSON
.
parse
(
screenBrightness
);
}
// 校验用户输入是否为空
checkData
(
screenBrightness
:
ScreenBrightness
)
{
if
(
screenBrightness
.
brightSwitch
===
''
||
screenBrightness
.
defaultValue
===
''
)
{
this
.
showToastMessage
(
$r
(
'
app.string.fruit_input_null_msg
'
));
return
true
;
}
return
false
;
}
// 点击保存按钮保存数据
writeData
(
screenBrightness
:
ScreenBrightness
)
{
// Check whether the data is null.
let
isDataNull
=
this
.
checkData
(
screenBrightness
);
if
(
isDataNull
)
{
return
;
}
// The data is inserted into the preferences database if it is not empty.
this
.
putPreference
(
screenBrightness
);
this
.
showToastMessage
(
$r
(
'
app.string.write_success_msg
'
));
}
// 消息弹框
showToastMessage
(
message
:
Resource
)
{
promptAction
.
showToast
({
message
:
message
,
duration
:
3000
});
};
}
```
2.
UI中主要包含两大部分:文本和输入框,按钮。将这两部分分别抽取为子组件,在主页中进行调用。具体代码如下:
文本和输入框子组件:
```
ts
import
ScreenBrightness
from
'
../common/bean/Brightness
'
;
@
Component
export
default
struct
TextItemComponent
{
private
textResource
:
Resource
;
private
placeholderResource
:
Resource
;
private
marginBottom
:
string
;
private
marginTop
:
string
;
private
textInputType
:
InputType
;
private
textFlag
:
number
;
@
Link
screenBrightness
:
ScreenBrightness
;
private
textInputCallBack
:
(
value
:
string
)
=>
void
;
aboutToAppear
()
{
if
(
this
.
textFlag
===
0
)
{
this
.
marginTop
=
'
8%
'
;
this
.
marginBottom
=
'
4%
'
;
this
.
textInputType
=
InputType
.
Normal
;
}
else
{
this
.
marginBottom
=
'
321vp
'
;
this
.
textInputType
=
InputType
.
Number
;
}
}
build
()
{
Column
()
{
// 文本
Text
(
this
.
textResource
)
.
fontSize
(
25
)
.
height
(
'
3.2%
'
)
.
width
(
'
100%
'
)
.
fontColor
(
"
#182431
"
)
.
letterSpacing
(
'
1.58
'
)
.
fontWeight
(
500
)
.
margin
({
bottom
:
'
2%
'
,
left
:
'
7%
'
,
top
:
this
.
marginTop
})
// 输入框
TextInput
({
placeholder
:
this
.
placeholderResource
,
text
:
this
.
textFlag
===
0
?
(
this
.
screenBrightness
.
brightSwitch
)
:
(
this
.
screenBrightness
.
defaultValue
)
})
.
placeholderFont
({
size
:
20
,
weight
:
500
})
.
placeholderColor
(
"
#BDC1C4
"
)
.
caretColor
(
Color
.
Blue
)
.
type
(
this
.
textInputType
)
.
height
(
'
7%
'
)
.
width
(
'
93%
'
)
.
margin
({
bottom
:
this
.
marginBottom
})
.
fontSize
(
20
)
.
fontColor
(
"
#182431
"
)
.
fontWeight
(
500
)
.
backgroundColor
(
"
#FFFFFF
"
)
.
onChange
((
value
:
string
)
=>
{
this
.
textInputCallBack
(
value
);
})
}
}
}
```
按钮子组件:
```
ts
import
PreferenceModel
from
'
../model/PreferenceModel
'
;
import
ButtonItemData
from
'
../common/bean/ButtonItemData
'
;
import
ScreenBrightness
from
'
../common/bean/Brightness
'
;
@
Component
export
default
struct
ButtonComponent
{
private
buttonItemValues
:
Array
<
ButtonItemData
>
=
this
.
getButtonItemValues
();
@
Link
screenBrightness
:
ScreenBrightness
;
build
()
{
Column
()
{
ForEach
(
this
.
buttonItemValues
,
(
item
)
=>
{
Button
(
item
.
text
,
{
type
:
ButtonType
.
Capsule
,
stateEffect
:
true
})
.
backgroundColor
(
"
#E8A027
"
)
.
width
(
'
87%
'
)
.
height
(
'
6%
'
)
.
fontWeight
(
500
)
.
fontSize
(
20
)
.
margin
({
bottom
:
'
24vp
'
})
.
onClick
(()
=>
{
item
.
clickMethod
();
})
},
item
=>
JSON
.
stringify
(
item
))
}
}
// 在foreach中渲染Button组件时传入不同按钮的参数
getButtonItemValues
()
{
let
values
:
Array
<
ButtonItemData
>
=
[
new
ButtonItemData
(
'
保存数据
'
,
()
=>
{
// 调用保存方法
PreferenceModel
.
writeData
(
this
.
screenBrightness
);
}
),
new
ButtonItemData
(
'
读取数据
'
,
()
=>
{
// 调用读取方法
PreferenceModel
.
getPreference
().
then
(
resultData
=>
{
this
.
screenBrightness
=
resultData
;
console
.
info
(
'
dbdata is
'
+
JSON
.
stringify
(
resultData
))
});
}
),
new
ButtonItemData
(
'
删除数据
'
,
()
=>
{
// 调用删除方法
PreferenceModel
.
deletePreferences
();
// 数据删除后将相关内容置为空
this
.
screenBrightness
.
brightSwitch
=
''
;
this
.
screenBrightness
.
defaultValue
=
''
}
)
];
return
values
;
}
}
```
3.
构建首页UI。
在页面生命周期的aboutToAppear中调用自定义首选项类的getPreference方法获取到保存的数据,这样如果用户之前有保存数据的话,进入应用中就可以显示之前保存的数据。
具体代码如下:
```
ts
import
PreferenceModel
from
'
../model/PreferenceModel
'
;
import
ButtonComponent
from
'
../view/ButtonComponent
'
;
import
TextItemComponent
from
'
../view/TextItemComponent
'
;
import
ScreenBrightness
from
'
../common/bean/Brightness
'
;
@
Entry
@
Component
struct
Setting
{
@
State
screenBrightness
:
ScreenBrightness
=
new
ScreenBrightness
(
''
,
''
);
build
()
{
Column
()
{
// 亮度调节文本及文本框
TextItemComponent
({
textResource
:
$r
(
'
app.string.brightness_text
'
),
placeholderResource
:
$r
(
'
app.string.brightness_placeholder
'
),
textFlag
:
0
,
screenBrightness
:
$screenBrightness
,
textInputCallBack
:
(
value
)
=>
{
this
.
screenBrightness
.
brightSwitch
=
value
;
}
})
// 设定值文本及文本框
TextItemComponent
({
textResource
:
$r
(
'
app.string.defaultValue_text
'
),
placeholderResource
:
$r
(
'
app.string.defaultValue_placeholder
'
),
textFlag
:
1
,
screenBrightness
:
$screenBrightness
,
textInputCallBack
:
(
value
)
=>
{
this
.
screenBrightness
.
defaultValue
=
value
;
}
})
// 按钮
ButtonComponent
({
screenBrightness
:
$screenBrightness
})
}
.
width
(
'
100%
'
)
.
height
(
'
100%
'
)
.
backgroundColor
(
"
#F1F3F5
"
)
}
async
aboutToAppear
()
{
await
PreferenceModel
.
getPreferencesFromStorage
();
// 获取到之前保存的数据,显示在输入框中
PreferenceModel
.
getPreference
().
then
(
resultData
=>
{
this
.
screenBrightness
=
resultData
;
});
}
}
```
## 完整代码
由于开发步骤中已经展示了大部分完整代码,此处补充前文中未呈现的两个数据类:
亮度数据类:
```
ts
export
default
class
ScreenBrightness
{
// 亮度调节
brightSwitch
:
string
;
// 设定值
defaultValue
:
string
;
constructor
(
brightSwitch
:
string
,
defaultValue
:
string
)
{
this
.
brightSwitch
=
brightSwitch
;
this
.
defaultValue
=
defaultValue
;
}
}
```
按钮数据类:
```
ts
export
default
class
ButtonItemData
{
// 按钮文本
text
:
string
;
// 按钮点击事件触发的方法
clickMethod
:
()
=>
void
;
constructor
(
text
:
string
,
clickMethod
:
()
=>
void
)
{
this
.
text
=
text
;
this
.
clickMethod
=
clickMethod
;
}
}
```
## 参考
-
[
@ohos.data.preferences (用户首选项)
](
../application-dev/reference/apis/js-apis-data-preferences.md
)
-
[
通过用户首选项实现数据持久化
](
../application-dev/database/data-persistence-by-preferences.md
)
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录