加载中...

HarmonyOS一多学习


AGC平台:

**华为AGC(AppGallery Connect)**是华为为开发者提供的一站式应用开发与运营服务平台,主要服务于华为生态(如HMS移动应用、HarmonyOS应用等)。

主要用途:华为应用上架,开通华为服务

  • 快速开发:提供开发工具、API和SDK,降低开发门槛。
  • 应用上架:支持应用发布到华为应用市场(AppGallery)。
  • 运营增长:助力开发者提升应用活跃度、用户留存和变现能力。

利用AGC平台创建项目(对内)+应用(对外,供别人进行下载)

应用包名(利用驼峰命名法,确保唯一性):com.公司域名.hm(此处是鸿蒙应用,如果是安卓可改为az)_**(应用名)

一、一多定义

img

“一次开发、多端部署”简称“一多”:一套代码工程,一次开发上架,多端按需部署。

为了实现这个目标,主要解决 3 个核心问题:

img

  1. 页面适配问题:界面级一多 - ArkUI(响应式布局/自适应布局)
  2. 功能兼容问题:功能级一多
  3. 工程如何组织:工程级一多 - 模块化按需部署(三层架构)

二、界面级一多能力

此处主要解决的是1.界面适配问题

img

界面级一多能力有 2 类:

  1. 自适应布局: 略微调整界面结构 - 第一个图片以及第二个图片,如果界面宽度增大,对应显示商品也会进行适应增加,界面略微进行调整
  2. **响应式布局:**比较大的界面调整 - 例如第三个平板界面(导航栏移动到左侧 界面进行了较大程度的调整)

2.1 自适应布局

img

2.1.1 自适应拉伸

2.1.1.1 拉伸能力(增大or减小区域分配给容器组件)

拉伸能力是指容器组件尺寸发生变化时,增加或减小的空间全部分配给容器组件内指定区域。

1> flexGrow / flexShrink

该部分重点为了解layoutweight与flenGrow / flexShrink之间的区别

**举一个例子:为Image图片添加layoutweight可以发现确实可以进行容器宽度的匹配,但是视觉效果不佳,**layoutweight会同时对组件进行缩小与放大

img

如果想要只在特定情况对组件进行增大或者缩小,就要使用到flexGrow / flexShrink

代码中为Row Image添加该属性,对Row添加.flexGrow(0).flexShrink(1)表示容器只会缩小不会增大

对Image添加.flexGrow(1).flexShrink(0)表示容器只会增大不会缩小

flexShrink/flexGrow

img

img

2.1.1.2 均分能力(区域分配给空白)

文字和开关的尺寸固定,仅有中间空白区域(Blank组件)随父容器尺寸变化而伸缩。

均分能力:blank

img

img

使用blank组件进行中间部分的拉伸

均分能力: justifyContent(FlexAlign.SpaceBetween)属性

img

通过设置该属性可以使组件按照父组件尺寸进行适应分布

img

2.1.2 自适应缩放

2.1.2.1 占比能力

占比能力是指子组件的宽高按照预设的比例,随父容器组件发生变化。– layoutweight属性

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
@Entry
@Component
struct ProportionCapabilitySample {
@State rate: number = 0.5


// 底部滑块,可以通过拖拽滑块改变容器尺寸
@Builder slider() {
Slider({ value: 100, min: 25, max: 50, style: SliderStyle.OutSet })
.blockColor(Color.White)
.width('60%')
.height(50)
.onChange((value: number) => {
this.rate = value / 100
})
.position({ x: '20%', y: '80%' })
}


build() {
Column() {
Column() {
Row() {
Column() {
Image($r("app.media.ic_public_play_last"))
.width(48)
.height(48)
}
.height(96)
.layoutWeight(1) // 设置子组件在父容器主轴方向的布局权重
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)


Column() {
Image($r("app.media.ic_public_pause"))
.width(48)
.height(48)
}
.height(96)
.layoutWeight(1) // 设置子组件在父容器主轴方向的布局权重
.backgroundColor('#66F1CCB8')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)


Column() {
Image($r("app.media.ic_public_play_next"))
.width(48)
.height(48)
}
.height(96)
.layoutWeight(1) // 设置子组件在父容器主轴方向的布局权重
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
.width(this.rate * 100 + '%')
.height(96)
.borderRadius(16)
.backgroundColor('#FFFFFF')
}


this.slider()
}
.width('100%')
.height('100%')
.backgroundColor('#F1F3F5')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}

2.1.2.2 缩放能力

缩放能力是指子组件的宽高按照预设的比例,随容器组件发生变化,且变化过程中子组件的宽高比不变

aspectRatio - 固定图片宽高比

不加该属性组件的宽高比会发生变化

img

添加aspectRatio()属性 - 图片宽高比固定

img

注意点:

有的时候会加载网络图片 - 网络速度慢的时候图片会加载不出来(例如下图如果网络图片加载不出来,文字会占据图片的位置) - 此时需要预留出马上要加载出来的图片位置 - aspectRatio即可实现预留图片位置

img

2.1.3 自适应延伸

2.1.3.1 延伸能力

img

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/adaptive-layout-V5#%E5%BB%B6%E4%BC%B8%E8%83%BD%E5%8A%9B

  1. 延伸能力使用List组件进行实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@Entry
@Component
struct ExtensionCapabilitySample1 {
@State rate: number = 0.60
readonly appList: number [] = [0, 1, 2, 3, 4, 5, 6, 7]


// 底部滑块,可以通过拖拽滑块改变容器尺寸
@Builder slider() {
Slider({ value: this.rate * 100, min: 8, max: 60, style: SliderStyle.OutSet })
.blockColor(Color.White)
.width('60%')
.height(50)
.onChange((value: number) => {
this.rate = value / 100
})
.position({ x: '20%', y: '80%' })
}


build() {
Column() {
Row({ space: 10 }) {
// 通过List组件实现隐藏能力
List({ space: 10 }) {
ForEach(this.appList, (item:number) => {
ListItem() {
Column() {
Image($r("app.media.startIcon")).width(48).height(48).margin({ top: 8 })
Text('App name')
.width(64)
.height(30)
.lineHeight(15)
.fontSize(12)
.textAlign(TextAlign.Center)
.margin({ top: 8 })
.padding({ bottom: 15 })
}.width(80).height(102)
}.width(80).height(102)
})
}
.padding({ top: 16, left: 10 })
.listDirection(Axis.Horizontal) // list列表的方向
// .lanes(2) // 交叉轴行列数
.width('100%')
.height(118)
.borderRadius(16)
.backgroundColor(Color.White)
}
.width(this.rate * 100 + '%')


this.slider()
}
.width('100%')
.height('100%')
.backgroundColor('#F1F3F5')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}

img

  1. 通过Scroll以及Row组件进行实现伸缩效果

实现思路: 在Row组件外嵌套一个Scroll容器组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@Entry
@Component
struct ExtensionCapabilitySample2 {
private scroller: Scroller = new Scroller()
@State rate: number = 0.60
@State appList: number [] = [0, 1, 2, 3, 4, 5, 6, 7]


// 底部滑块,可以通过拖拽滑块改变容器尺寸
@Builder slider() {
Slider({ value: this.rate * 100, min: 8, max: 60, style: SliderStyle.OutSet })
.blockColor(Color.White)
.width('60%')
.height(50)
.onChange((value: number) => {
this.rate = value / 100;
})
.position({ x: '20%', y: '80%' })
}


build() {
Column() {
// 通过Scroll和Row组件实现隐藏能力
Scroll(this.scroller) {
Row({ space: 10 }) {
ForEach(this.appList, () => {
Column() {
Image($r("app.media.startIcon")).width(48).height(48).margin({ top: 8 })
Text('App name')
.width(64)
.height(30)
.lineHeight(15)
.fontSize(12)
.textAlign(TextAlign.Center)
.margin({ top: 8 })
.padding({ bottom: 15 })
}.width(80).height(102)
})
}
.padding({ top: 16, left: 10 })
.height(118)
.backgroundColor(Color.White)
}
.scrollable(ScrollDirection.Horizontal)
.borderRadius(16)
.width(this.rate * 100 + '%')
this.slider()
}
.width('100%')
.height('100%')
.backgroundColor('#F1F3F5')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}

2.1.3.2 隐藏能力

隐藏能力是指容器组件内的子组件,按照其预设的显示优先级,随容器组件尺寸变化显示或隐藏,其中相同显示优先级的子组件同时显示或隐藏。.displayPriority(2) // 布局优先级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@Entry
@Component
struct HiddenCapabilitySample {
@State rate: number = 0.45


// 底部滑块,可以通过拖拽滑块改变容器尺寸
@Builder slider() {
Slider({ value: this.rate * 100, min: 10, max: 45, style: SliderStyle.OutSet })
.blockColor(Color.White)
.width('60%')
.height(50)
.onChange((value: number) => {
this.rate = value / 100
})
.position({ x: '20%', y: '80%' })
}


build() {
Column() {
Row({ space:24 }) {
Image($r("app.media.ic_public_favor"))
.width(48)
.height(48)
.objectFit(ImageFit.Contain)
.displayPriority(1) // 布局优先级


Image($r("app.media.ic_video_grid_9"))
.width(48)
.height(48)
.objectFit(ImageFit.Contain)
.displayPriority(2) // 布局优先级


Image($r("app.media.ic_video_grid_8"))
.width(48)
.height(48)
.objectFit(ImageFit.Contain)
.displayPriority(3) // 布局优先级


Image($r("app.media.ic_video_grid_6"))
.width(48)
.height(48)
.objectFit(ImageFit.Contain)
.displayPriority(2) // 布局优先级


Image($r("app.media.ic_video_grid_3"))
.width(48)
.height(48)
.objectFit(ImageFit.Contain)
.displayPriority(1) // 布局优先级
}
.width(this.rate * 100 + '%')
.height(96)
.borderRadius(16)
.backgroundColor('#FFFFFF')
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Center)


this.slider()
}
.width('100%')
.height('100%')
.backgroundColor('#F1F3F5')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}

实现:

根据代码中设置的优先级,当父容器宽度缩小时会首先隐藏优先级小的组件

img

img

注意

设置优先级一般都要进行设置,如果不进行设置会默认为最小级

2.1.4 自适应折行

2.1.4.1 折行能力

Flex组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@Entry
@Component
struct WrapCapabilitySample {
@State rate: number = 0.7
readonly imageList: Resource [] = [
$r('app.media.ic_video_grid_7'),
$r('app.media.ic_video_grid_7'),
$r('app.media.ic_video_grid_7'),
$r('app.media.ic_video_grid_7'),
$r('app.media.ic_video_grid_7'),
$r('app.media.ic_video_grid_7')
]


// 底部滑块,可以通过拖拽滑块改变容器尺寸
@Builder slider() {
Slider({ value: this.rate * 100, min: 50, max: 70, style: SliderStyle.OutSet })
.blockColor(Color.White)
.width('60%')
.onChange((value: number) => {
this.rate = value / 100
})
.position({ x: '20%', y: '87%' })
}


build() {
Flex({ justifyContent: FlexAlign.Center, direction: FlexDirection.Column }) {
Column() {
// 通过Flex组件warp参数实现自适应折行
// TODO 为什么不推荐使用flex进行布局 - 性能不如线性布局(因为需要计算什么时候需要进行换行,多了一个计算的过程)
Flex({
// TODO 需要进行折行的时候用flex组件
direction: FlexDirection.Row, //flex布局方向
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center,
wrap: FlexWrap.Wrap //折行能力
}) {
ForEach(this.imageList, (item:Resource) => {
Image(item).width(183).height(138).padding(10)
})
}
.backgroundColor('#FFFFFF')
.padding(20)
.width(this.rate * 100 + '%')
.borderRadius(16)
}
.width('100%')


this.slider()
}.width('100%')
.height('100%')
.backgroundColor('#F1F3F5')
}
}

imgimg

tips

为什么不推荐使用flex进行布局 - 性能不如线性布局(因为需要计算什么时候需要进行换行,多了一个计算的过程)

2.2 响应式布局

比较大的界面调整

自适应布局可以保证窗口尺寸在【一定范围内变化】时,页面的显示是正常的。但是将窗口尺寸【变化较大】时(如窗口宽度从400vp变化为1000vp),仅仅依靠自适应布局可能出现图片异常放大或页面内容稀疏、留白过多等问题,此时就需要借助响应式布局能力调整页面结构。

响应式布局是指页面内的元素可以根据特定的特征(如窗口宽度、屏幕方向等)自动变化以适应外部容器变化的布局能力。

img

第一种是网格布局:网格布局可以有效的控制需要排版的行和列数,也可设置行列之间的间距

第二种栅格布局:栅格布局相比于网格布局功能更加强大

基础写法:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-container-gridrow-V5#gutteroption

网格布局 Grid(网格布局容器) - GridItem(网格布局项)

栅格布局 GridRow(栅格布局容器) - GridCol(栅格布局项)

对比:

1.GridRow 设行列数和间距更简单

2.GridRow 支执点,响应式布局

3.GridCol 支持元素排序,所占列数

img

2.2.1栅格布局(GridRow/GridCol)

设置断点(sm md lg)

GridRowColumnOption

栅格在不同宽度设备类型下,栅格列数。

卡片能力: 从API version 9开始,该接口支持在ArkTS卡片中使用。

元服务API: 从API version 11开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

名称 类型 必填 说明
xs number 在栅格大小为xs的设备上,栅格容器组件的栅格列数。
sm number 在栅格大小为sm的设备上,栅格容器组件的栅格列数。
md number 在栅格大小为md的设备上,栅格容器组件的栅格列数。
lg number 在栅格大小为lg的设备上,栅格容器组件的栅格列数。
xl number 在栅格大小为xl的设备上,栅格容器组件的栅格列数。
xxl number 在栅格大小为xxl的设备上,栅格容器组件的栅格列数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Entry
@Component
struct Index {
build() {
Column() {
// 栅格布局 - columns的对应属性
// sm为当当前的设备为小型设备的时候(例如手机):有2列
// md为当当前的设备为中型设备的时候(例如折叠屏):有3列
// lg为当当前的设备为大型设备的时候(例如平板):有4列
GridRow({ columns: { sm: 2, md: 3, lg: 4 }, gutter: { x: 10, y: 10 } }) {
ForEach(Array.from({ length: 10 }), () => {
GridCol() {
Column() {
Image($r('app.media.ic_video_grid_5'))
.width('100%')
.aspectRatio(1072 / 616)
.borderRadius(10)
Text('标题')
.fontSize(14)
}
}
})
}
}
}
}

imgimg

监听断点变化

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-gridrow#onbreakpointchange

1
2
3
.onBreakpointChange((breakpoints: string) => {
this.breakpoints = breakpoints
})

img

控制子元素所占列数

通过span控制所占列数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Entry
@Component
struct Index {
@State breakpoints: string = ''

build() {
Column() {
// 栅格布局 - columns的对应属性
// sm为当当前的设备为小型设备的时候(例如手机):有2列
// md为当当前的设备为中型设备的时候(例如折叠屏):有3列
// lg为当当前的设备为大型设备的时候(例如平板):有4列
GridRow({
columns: 12,
gutter: 10
}) {
ForEach(Array.from({ length: 12 }), () => {
// 栅格布局 通过span控制所占列数
GridCol({
span: {
sm: 6,
md: 4,
lg: 2
}
}) {
Column() {
Image($r('app.media.ic_video_grid_5'))
.width('100%')
.aspectRatio(1072 / 616)
.borderRadius(10)
Text('标题')
.fontSize(14)
}
}
})
}
.onBreakpointChange((breakpoints: string) => {
this.breakpoints = breakpoints
})

Text('当前断点' + this.breakpoints)
.fontSize(46)
}
}
}

img

2.2.2 断点

2.2.2.1 全局断点

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/responsive-layout

核心三步走:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// MainAbility.ts
import { window, display } from '@kit.ArkUI'
import { UIAbility } from '@kit.AbilityKit'

export default class MainAbility extends UIAbility {
private curBp: string = ''
//...
// 根据当前窗口尺寸更新断点
private updateBreakpoint(windowWidth: number) :void{
try {
// 核心代码1: 将长度的单位由px换算为vp,(px除以像素密度得到vp)
let windowWidthVp = windowWidth / display.getDefaultDisplaySync().densityPixels
let newBp: string = ''
// 核心代码2: 基于窗口宽度vp值,判断当前设备属于哪个断点范围
if (windowWidthVp < 320) {
newBp = 'xs'
} else if (windowWidthVp < 600) {
newBp = 'sm'
} else if (windowWidthVp < 840) {
newBp = 'md'
} else {
newBp = 'lg'
}
if (this.curBp !== newBp) {
this.curBp = newBp
// 核心代码3: 使用状态变量记录当前断点值
AppStorage.setOrCreate('currentBreakpoint', this.curBp)
}
} catch(err) {
console.log("getDisplayByIdSync failed err" + err.code)
}
}

onWindowStageCreate(windowStage: window.WindowStage) :void{
windowStage.getMainWindow().then((windowObj) => {
// 获取应用启动时的窗口尺寸
this.updateBreakpoint(windowObj.getWindowProperties().windowRect.width)
// 注册回调函数,监听窗口尺寸变化
windowObj.on('windowSizeChange', (windowSize)=>{
this.updateBreakpoint(windowSize.width)
})
});
// ...
}

//...
}
  1. 页面中使用断点信息
1
2
3
4
@Entry
@Component
struct Demo11_login {
@StorageProp('currentBreakpoint') curBp: string = 'sm'

2.2.2.2 封装系统工具-BreakPointType

封装系统工具-BreakPointType

* 1. 如果是 两种的情况:用 三元表达式 即可

​ 2. 如果是 多种的情况:用 三元表达式 就不太方便啦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// common/breakpointSystem.ets
/*
封装系统工具-BreakPointType
* 1. 如果是 两种的情况:用 三元表达式 即可
2. 如果是 多种的情况:用 三元表达式 就不太方便啦
*/

export interface BreakPointTypeOption<T> {
xs?: T
sm?: T
md?: T
lg?: T
}

export class BreakPointType<T> {
options: BreakPointTypeOption<T>

constructor(option: BreakPointTypeOption<T>) {
this.options = option
}

getValue(currentBreakPoint: string) {
if (currentBreakPoint === 'xs') {
return this.options.xs
} else if (currentBreakPoint === 'sm') {
return this.options.sm
} else if (currentBreakPoint === 'md') {
return this.options.md
} else if (currentBreakPoint === 'lg') {
return this.options.lg
} else {
return undefined
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 1. 导入BreakPointType
import { BreakPointType } from '../commons/breakpointSystem'


@Entry
@Component
struct Index {
// 2. 通过 AppStorage 获取断点值
@StorageProp('currentBreakpoint') currentBreakpoint: string = 'sm'

build() {
Column() {
Text(this.currentBreakpoint)
}
.width(200)
.height(200)
.backgroundColor(
// 3. 实例化 设置不同断点的取值,并通过 getValue 根据当前断点值对应的值
new BreakPointType({
xs: Color.Red,
sm: Color.Yellow,
md: Color.Blue,
lg: Color.Green
}).getValue(this.currentBreakpoint)
)
}
}

img

img

案例说明:

运用了上面封装的工具类进行多种设备的界面适配(例如sm型号为两列 md/lg型号为4列)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import { BreakPointType } from '../commons/breakpointSystem'

interface MovieItem {
title: string
img: ResourceStr
}

@Entry
@Component
struct Index {
items: MovieItem[] = [
{ title: '电影标题1', img: $r('app.media.ic_video_grid_1') },
{ title: '电影标题2', img: $r('app.media.ic_video_grid_2') },
{ title: '电影标题3', img: $r('app.media.ic_video_grid_3') },
{ title: '电影标题4', img: $r('app.media.ic_video_grid_4') },
{ title: '电影标题5', img: $r('app.media.ic_video_grid_5') },
{ title: '电影标题6', img: $r('app.media.ic_video_grid_6') },
{ title: '电影标题7', img: $r('app.media.ic_video_grid_7') },
{ title: '电影标题8', img: $r('app.media.ic_video_grid_8') },
{ title: '电影标题9', img: $r('app.media.ic_video_grid_9') },
{ title: '电影标题10', img: $r('app.media.ic_video_grid_10') },
]
@StorageProp('currentBreakpoint') currentBreakpoint: string = 'sm'

build() {
Grid() {
ForEach(this.items, (item: MovieItem) => {
GridItem() {
Column({ space: 10 }) {
Image(item.img)
.borderRadius(10)
Text(item.title)
.width('100%')
.fontSize(20)
.fontWeight(600)

}
}
})
}
.columnsTemplate(new BreakPointType({
xs: '1fr 1fr',
sm: '1fr 1fr',
md: '1fr 1fr 1fr',
lg: '1fr 1fr 1fr 1fr'
}).getValue(this.currentBreakpoint)
)
.rowsGap(10)
.columnsGap(10)
.padding(10)
}
}

imgimg

2.2.3 媒体查询

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-layout-development-media-query-V5

核心步骤:创建监听器 - 添加回调函数(当result中的matches值为true时表示匹配成功)此时可以进行变量的赋值操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 媒体查询
import { mediaquery } from '@kit.ArkUI'

// 1. 创建监听器
const listenerXS: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(0vp<=width<320vp)');

@Entry
@Component
struct Index {
// 注册监听器
aboutToAppear(): void {
// 添加回调函数
listenerXS.on('change', (result: mediaquery.MediaQueryResult) => {
console.log('listenerXS result:', JSON.stringify(result))
})
}

build() {
Column() {
Text('hello world')
}
}
}

img

listenerXS result: {“matches”:false,”media”:”(0vp<=width<320vp)”}

说明:match进行匹配 - 判断是否符合监听器括号中的条件(因为开的是平板所以输出为false)

media为输出监听器的条件

为了提高性能,在界面被销毁时应该移除监听器

1
2
3
4
5
6
7
aboutToDisappear(): void {
// 移除监听器
listenerXS.off('change')
listenerSM.off('change')
listenerMD.off('change')
listenerLG.off('change')
}

2.2.4 总结概括

img

三、功能级一多

不同设备的系统能力有差异,如智能穿戴设备是否具备定位能力、智慧屏是否具备摄像头等,功能如何兼容。

什么是系统能力

系统能力(即SystemCapability,缩写为SysCap)指操作系统中每一个相对独立的特性,如蓝牙,WIFI,NFC,摄像头等,都是系统能力之一。每个系统能力对应多个API,随着目标设备是否支持该系统能力共同存在或消失。

3.1 canIUse判断

1
2
3
4
5
6
if (canIUse("能力集的名字")) {
// 正常调用
} else {
// 提示用户
console.log("该设备不支持SystemCapability.Communication.NFC.Core")
}

eg1:判断是否具有NFC系统功能

1
2
3
4
5
6
7
8
9
10
11
12
// 如果某个系统能力没有写入应用的要求能力集中,那么在使用前需要判断设备是否支持该系统能力。
aboutToAppear(): void {
// 方法1:使用canUse接口判断设备是否支持某系统能力
// TODO if (canIUse("能力集的名字"))
if (canIUse("SystemCapability.Communication.NFC.Core")) {
console.log("该设备支持SystemCapability.Communication.NFC.Core");
} else {
console.log("该设备不支持SystemCapability.Communication.NFC.Core");
}


}

eg2:display.isFoldable() 这个 api 并不是每个设备都可以使用,在调用之前就可以先判断一下

img

isFoldable不是所有设备都可以 - 所以会显示高亮效果

调用canIUse接口判断是否具有该系统功能,可以将鼠标移动至isFoldable处查看如何在canIUse中写系统功能

1
2
3
4
// display.isFoldable()判断设备是否为折叠屏
if (canIUse('SystemCapability.Window.SessionManager')) {
display.isFoldable()
}

img

四、工程级一多

4.1 概念

一多模式下,官方推荐在开发过程中采用”三层工程架构”,其实就是把项目拆分成不同类型的模块,再通过模块之间的引用组合,最终实现应用功能,拆分规范如下:

img

4.2 选择合适的包类型

HAP、HAR、HSP三者的功能和使用场景总结对比如下:

Module类型 包类型 说明
Ability HAP 应用的功能模块,可以独立安装和运行,必须包含一个entry类型的HAP,可选包含一个或多个feature类型的HAP。
Static Library HAR 静态共享包,编译态复用。- 支持应用内共享,也可以发布后供其他应用使用。- 作为二方库,发布到OHPM私仓,供公司内部其他应用使用。- 作为三方库,发布到OHPM中心仓,供其他应用使用。- 多包(HAP/HSP)引用相同的HAR时,会造成多包间代码和资源的重复拷贝,从而导致应用包膨大。- 注意:编译HAR时,建议开启混淆能力,保护代码资产。
Shared Library HSP 动态共享包,运行时复用。- 当前仅支持应用内共享。- 当多包(HAP/HSP)同时引用同一个共享包时,采用HSP替代HAR,可以避免HAR造成的多包间代码和资源的重复拷贝,从而减小应用包大小。

1> HAP:应用的功能模块,可以独立安装和使用(必须包含一个entry + 可选一个或者多个feature)

2> HAR:静态共享包,编译时复用(应用内共享,可发布后供其他应用使用)(三方库,公司私仓)

tip:如果远程仓库下载不下来可以直接在该文件中进行配置:

img

配置完毕即可查看下载下来的包:

img

注意:多包(HAP/HSP)引用相同的HAR时,会造成多包间代码和资源的重复拷贝,从而导致应用包膨大。

建议下载到commons模块,因为features和products都可以进行调用(如果下载到products模块只能在products进行使用,commons或者features模块想要进行使用还需要重新进行下载,导致代码冗余)


文章作者: 太阳神小赖
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 太阳神小赖 !
  目录