未验证 提交 90d0893c 编写于 作者: O openharmony_ci 提交者: Gitee

!13129 一多文档刷新

Merge pull request !13129 from fanzhaonan/one_multi
......@@ -39,6 +39,7 @@
- 典型页面场景
- [应用市场首页](appgallery-home-page.md)
- [音乐专辑页](music-album-page.md)
- [设置应用页面](settings-application-page.md)
- [交互归一](interaction-event-normalization.md)
- [多态组件](polymorphic-controls.md)
- [资源使用](resource-usage.md)
......
......@@ -6,18 +6,18 @@
设备类型分为default(默认设备)、tablet、tv、wearable等,有多种查询设备类型的方式。
1. 通过命令行的方式查询设备类型。
通过命令行查询指定系统参数(const.build.characteristics)进而确定设备类型,详见[系统参数介绍](../../../device-dev/subsystems/subsys-boot-init-sysparam.md)
通过命令行查询指定系统参数(const.product.devicetype)进而确定设备类型,详见[系统参数介绍](../../../device-dev/subsystems/subsys-boot-init-sysparam.md)
```shell
# 方法一
hdc shell param get "const.build.characteristics"
hdc shell param get "const.product.devicetype"
# 方法二
hdc shell cat /etc/param/ohos.para | grep const.build.characteristic
hdc shell cat /etc/param/ohos.para | grep const.product.devicetype
```
2. 在应用开发过程中查询设备类型。
- 通过js接口查询指定系统参数(const.build.characteristics)进而确定设备类型,详见[系统属性](../../reference/apis/js-apis-system-parameter.md)
- 通过js接口查询指定系统参数(const.product.devicetype)进而确定设备类型,详见[系统属性](../../reference/apis/js-apis-system-parameter.md)
```typescript
import parameter from '@ohos.systemparameter'
......@@ -29,7 +29,7 @@
aboutToAppear() {
try {
this.deviceType = parameter.getSync("const.build.characteristics")
this.deviceType = parameter.getSync("const.product.devicetype")
} catch(e) {
console.log("getSync unexpected error: " + e)
}
......@@ -179,3 +179,57 @@ hdc shell reboot
}
}
```
## 如何获取组件的尺寸
实际开发过程中,开发者可能有获取页面中某个组件或某块区域的尺寸的诉求,以便通过手动计算等进行更精确的布局计算及优化。
开发者可以通过[组件区域变化事件](../../reference/arkui-ts/ts-universal-component-area-change-event.md)(即组件显示的尺寸、位置等发生变化时触发的事件)来获取指定组件的尺寸。
如下所示,通过onAreaChange事件获取Row组件(页面中白色区域)的尺寸。
![](figures/onAreaChange.gif)
```
@Entry
@Component
struct OnAreaChangeSample {
@State rate: number = 0.8
@State info: string = ''
// 底部滑块,可以通过拖拽滑块改变容器尺寸
@Builder slider() {
Slider({ value: this.rate * 100, min: 30, max: 80, style: SliderStyle.OutSet })
.blockColor(Color.White)
.width('60%')
.onChange((value: number) => {
this.rate = value / 100;
})
.position({ x: '20%', y: '80%' })
}
build() {
Column() {
Column() {
Row() {
Text(this.info).fontSize(20).lineHeight(22)
}
.borderRadius(12)
.padding(24)
.backgroundColor('#FFFFFF')
.width(this.rate * 100 + '%')
.onAreaChange((oldValue: Area, newValue: Area) => {
this.info = JSON.stringify(newValue)
})
}
this.slider()
}
.width('100%')
.height('100%')
.backgroundColor('#F1F3F5')
.justifyContent(FlexAlign.Center)
}
}
```
......@@ -25,8 +25,9 @@
从第4章开始将从UX设计、系统能力等角度,详尽的解答上述问题。
> **说明:**
>
> - 应用开发不仅包含应用页面开发,还包括应用后端功能开发以及服务器端开发等。
>
>
> - 本文旨在指导开发者如何在OpenHarmony系统中开发“一多”应用,服务器端开发不在本文探讨范围内。
......@@ -34,28 +35,9 @@
为了更好的阅读后面的章节,本小节主要介绍了一些基础知识,方便读者理解内容。
### 应用程序包结构
OpenHarmony 的应用以APP Pack (Application Package) 形式发布,它是由一个或多个HAP以及描述每个HAP属性的pack.info文件组成。
HAP是OpenHarmony的安装包,一个HAP在工程目录中对应一个Module,由Module编译而来,可分为entry和feature两种类型的HAP。
- **entry**:应用的主模块包。一个APP中,对于同一设备类型,可以有一个或多个entry类型的HAP,来支持该设备类型中不同规格(如API版本、屏幕规格等)的具体设备。
- **feature**:应用的动态特性模块包。一个APP Pack可以包含零个、一个或多个feature类型的HAP。
![zh-cn_image_0000001266965046](figures/zh-cn_image_0000001266965046.png)
> **说明:**
> - Module是开发者开发的相对独立的功能模块,由代码、资源、第三方库及应用配置文件组成,属于IDE开发视图的概念。Module分为entry、feature及har三种类型,相应的可以编译生成entry类型的HAP、feature类型的HAP,以及har包。
>
> - 如果需要了解应用程序包结构更多详情,可以查看[包结构说明](../../quick-start/application-package-structure-stage.md)。
### 方舟开发框架
OpenHarmony提供了方舟开发框架(简称:ArkUI),提供开发者进行应用UI开发时所必须的能力。
OpenHarmony提供了[方舟开发框架](../../ui/arkui-overview.md)(简称:ArkUI),提供开发者进行应用UI开发时所必须的能力。
方舟开发框架提供了两种开发范式,分别是基于JS扩展的类Web开发范式(后文中简称为“类Web开发范式”)和基于ArkTS的声明式开发范式(后文中简称为“声明式开发范式”)。
......@@ -63,7 +45,6 @@ OpenHarmony提供了方舟开发框架(简称:ArkUI),提供开发者进
- **类Web开发范式**:采用经典的HML、CSS、JavaScript三段式开发方式。使用HML标签文件进行布局搭建,使用CSS文件进行样式描述,使用JavaScript文件进行逻辑处理。UI组件与数据之间通过单向数据绑定的方式建立关联,当数据发生变化时,UI界面自动触发更新。此种开发方式,更接近Web前端开发者的使用习惯,快速将已有的Web应用改造成方舟开发框架应用。主要适用于界面较为简单的中小型应用开发。
两种开发范式的对比如下。
| **开发范式名称** | **语言生态** | **UI更新方式** | **适用场景** | **适用人群** |
......@@ -72,10 +53,26 @@ OpenHarmony提供了方舟开发框架(简称:ArkUI),提供开发者进
| 类Web开发范式 | JS语言 | 数据驱动更新 | 界面较为简单的中小型应用和卡片 | Web前端开发人员 |
> **说明:**
> - 声明式开发范式占用内存更少,**更推荐开发者选用声明式开发范式来搭建应用UI界面**。
>
> - 可以查看[方舟开发框架概述](../../ui/arkui-overview.md),了解方舟开发框架更多详情。
> 声明式开发范式占用内存更少,**更推荐开发者选用声明式开发范式来搭建应用UI界面**。
### 应用程序包结构
在进行应用开发时,一个应用通常包含一个或多个Module。Module是OpenHarmony应用/服务的基本功能单元,包含了源代码、资源文件、第三方库及应用/服务配置文件,每一个Module都可以独立进行编译和运行。
Module分为“Ability”和“Library”两种类型:
* “Ability”类型的Module编译后生成HAP包。
* “Library”类型的Module编译后生成HAR包。
OpenHarmony的应用以APP Pack形式发布,其包含一个或多个HAP包。HAP是OpenHarmony应用安装的基本单位,HAP可以分为Entry和Feature两种类型:
* Entry类型的HAP:应用的主模块。在同一个应用中,同一设备类型只支持一个Entry类型的HAP,通常用于实现应用的入口界面、入口图标、主特性功能等。
* Feature类型的HAP:应用的动态特性模块。Feature类型的HAP通常用于实现应用的特性功能,一个应用程序包可以包含一个或多个Feature类型的HAP,也可以不包含。
> **说明:**
> 关于Entry类型的HAP包、Feature类型的HAP包、HAR包以及APP Pack的详细介绍请参考[应用程序包结构说明](../../quick-start/application-package-structure-stage.md) 。
### 部署模型
......@@ -85,79 +82,65 @@ OpenHarmony提供了方舟开发框架(简称:ArkUI),提供开发者进
- **部署模型B**:不同类型的设备上按照一定的工程结构组织方式,通过一次编译生成**不同**的HAP(或HAP组合)。
建议开发者从设备类型及应用功能两个维度,结合具体的业务场景,考虑选择哪种部署模型。但不管采用哪种部署模型,都应该采用一次编译
开发者可以从应用UX设计及应用功能两个维度,结合具体的业务场景,考虑选择哪种部署模型。当然,也可以借助设备类型分类,快速做出判断
**设备类型**
从屏幕尺寸、交互方式及使用距离三个维度考虑,我们将常用的设备分为三大泛类:
从屏幕尺寸、输入方式及交互距离三个维度考虑,可以将常用类型的设备分为不同泛类:
- 默认设备、平板
- 车机、智慧屏
- 智能穿戴
对于相同泛类的设备,优先选择部署模型A,对于不同泛类设备,优先选择部署模型B。
**应用功能**
- ……
- 方舟开发框架提供了丰富的多设备适配能力,相同泛类的设备通常总是可以使用部署模型A。部署模型A需要的开发和维护工作量更小,而且可以保证不同类型设备上体验的一致性。
- 仅当同一泛类不同类型设备上规划的功能差异非常大时,才推荐使用部署模型B,如默认设备和平板分别交给两个团队设计、开发和维护等。
一般应用在不同设备上选择部署模型的思路如下:
![zh-cn_image_0000001400300617](figures/zh-cn_image_0000001400300617.png)
**对于相同泛类的设备,优先选择部署模型A,对于不同泛类设备,优先选择部署模型B。**
> **说明:**
> 页面导航逻辑是指应用内页面之间的跳转关系。假设默认设备上页面A跳转到页面B,平板设备上也是页面A跳转到页面B。因为两种设备屏幕大小不同,默认设备上页面B是覆盖显示在页面A上的,平板设备上页面B是在页面A的右边并且同时显示,但因为都是页面A跳转到页面B,那么我们认为它们的页面导航逻辑相同。
>
> * 应用在不同泛类设备上的UX设计或功能相似时,可以使用部署模型A。
> * 应用在同一泛类不同类型设备上UX设计或功能差异非常大时,可以使用部署模型B,但同时也应审视应用的UX设计及功能规划是否合理。
>
> * 本小节引入部署模型A和部署模型B的概念是为了方便开发者理解。实际上在开发多设备应用时,如果目标设备类型较多,往往是部署模型A和部署模型B混合使用。
>
> * 不管采用哪种部署模型,都应该采用一次编译。
**工程结构**
### 工程结构
“一多”推荐在应用开发过程中使用如下的“三层工程结构”。
- common:公共特性目录,如工具类、公共配置等。
- features:功能模块目录,存放应用中相对独立的各个功能的实现(包括该功能相关的UI代码及业务逻辑代码),如帐户管理等。
- common(公共能力层):用于存放公共基础能力集合(如工具库、公共配置等)。
- product:产品层目录,通过引用common和feature目录中代码的方式做功能和特性的集成,同时也作为主入口
common层不可分割,需编译成一个HAR包,其只可以被products和features依赖,不可以反向依赖
> **说明:**
> features层可横向调用和依赖common层能力;product层不可横向调用,可依赖features层和common层,且不能有反向依赖。
- features(基础特性层):用于存放基础特性集合(如应用中相对独立的各个功能的UI及业务逻辑实现等)。
部署模型不同,相应的代码工程结构也有差异。部署模型A和部署模型B的主要差异点集中在product层:
各个feature高内聚、低耦合、可定制,供产品灵活部署。不需要单独部署的feature通常编译为HAR包,供products或其它feature使用。需要单独部署的feature通常编译为Feature类型的HAP包,和products下Entry类型的HAP包进行[组合部署](../../quick-start/multi-hap-objective.md)。features层可以横向调用及依赖common层,同时可以被products层不同设备形态的HAP所依赖,但是不能反向依赖products层。
- 部署模型A可以直接在product目录中做功能和特性集成。
- products(产品定制层):用于针对不同设备形态进行功能和特性集成。
- 部署模型B需要在product目录下再建一级子目录,在不同的子目录中对不同的产品做差异化的功能和特性集成
products层各个子目录各自编译为一个Entry类型的HAP包,作为应用主入口。products层不可以横向调用
部署模型A对应的代码工程结构抽象后一般如下所示:
代码工程结构抽象后一般如下所示:
```
/application
├── common # 可选。公共特性目录, har类型的module
├── features # 可选。功能模块目录
│ ├── feature1 # 子功能1, har类型的module
│ ├── feature2 # 子功能2, har类型的module
├── common # 可选。公共能力层, 编译为HAR包
├── features # 可选。基础特性层
│ ├── feature1 # 子功能1, 编译为HAR包或Feature类型的HAP包
│ ├── feature2 # 子功能2, 编译为HAR包或Feature类型的HAP包
│ └── ...
└── product # 必选。产品层目录, entry类型的module,编译后为HAP
```
部署模型B对应的代码工程结构抽象后一般如下所示:
```
/application
├── common # 可选。公共特性目录, har类型的module
├── features # 可选。功能模块目录
│ ├── feature1 # 子功能1, har类型的module
│ ├── feature2 # 子功能2, har类型的module
│ └── ...
└── product # 必选。产品层目录
├── wearable # 智能穿戴泛类目录, entry类型的module,编译后为HAP
├── default # 默认设备泛类目录, entry类型的module,编译后为HAP
└── products # 必选。产品定制层
├── wearable # 智能穿戴泛类目录, 编译为Entry类型的HAP包
├── default # 默认设备泛类目录, 编译为Entry类型的HAP包
└── ...
```
> **说明:**
> 无论是用部署模型A还是部署模型B,在开发阶段,都应考虑**不同类型设备间最大程度的复用代码**,以减少开发及后续维护的工作量。
>
> * 部署模型不同,相应的代码工程结构也有差异。部署模型A和部署模型B的主要差异点集中在product层:部署模型A在product目录下同一子目录中做功能和特性集成;部署模型B在product目录下不同子目录中对不同的产品做差异化的功能和特性集成。
>
>
> * 开发阶段应考虑**不同类型设备间最大程度的复用代码**,以减少开发及后续维护的工作量。
> * 整个代码工程最终构建出一个[APP包](#应用程序包结构),应用以APP包的形式发布到应用市场中。
......@@ -9,6 +9,8 @@
| 自适应布局 | 当外部容器大小发生变化时,元素可以**根据相对关系自动变化**以适应外部容器变化的布局能力。相对关系如占比、固定宽高比、显示优先级等。当前自适应布局能力有7种:[拉伸能力](adaptive-layout.md#拉伸能力)[均分能力](adaptive-layout.md#均分能力)[占比能力](adaptive-layout.md#占比能力)[缩放能力](adaptive-layout.md#缩放能力)[延伸能力](adaptive-layout.md#延伸能力)[隐藏能力](adaptive-layout.md#隐藏能力)[折行能力](adaptive-layout.md#折行能力)。自适应布局能力可以实现界面显示随外部容器大小连续变化。 |
| 响应式布局 | 当外部容器大小发生变化时,元素可以**根据断点、栅格或特定的特征(如屏幕方向、窗口宽高等)自动变化**以适应外部容器变化的布局能力。当前响应式布局能力有3种:[断点](responsive-layout.md#断点)[媒体查询](responsive-layout.md#媒体查询)[栅格布局](responsive-layout.md#栅格布局)。响应式布局可以实现界面随外部容器大小有级不连续变化,通常不同特征下的界面显示会有较大的差异。 |
> **说明:**
> 自适应布局多用于解决页面各区域内的布局差异,响应式布局多用于解决页面各区域间的布局差异。
自适应布局和响应式布局常常需要借助容器类组件实现,或与容器类组件搭配使用。
......@@ -25,4 +27,3 @@
接下来将依次介绍自适应布局和响应式布局,同时结合实际,通过典型布局场景以及典型页面场景详细介绍两种布局能力的用法。
......@@ -31,8 +31,8 @@
| ------------------------------------------------------------ | -------------- | ------------------------------------------------------------ |
| [页面开发一多能力](https://gitee.com/openharmony/applications_app_samples/tree/master/MultiDeviceAppDev/AdaptiveCapabilities) | 声明式开发范式 | 本章配套的示例代码,包括自适应布局、响应式布局、典型布局场景以及资源文件使用等。 |
| [页面开发一多能力](https://gitee.com/openharmony/applications_app_samples/tree/master/MultiDeviceAppDev/JsAdaptiveCapabilities) | 类Web开发范式 | 本章配套的示例代码,包括自适应布局、响应式布局及资源文件使用等。 |
| [天气](https://gitee.com/openharmony/applications_app_samples/tree/master/MultiDeviceAppDev/Weather) | 声明式开发范式 | 一多示例应用,以天气应用为例,演示如何使用一多能力实现包含多个页面的应用。 |
| [应用市场首页](https://gitee.com/openharmony/applications_app_samples/tree/master/MultiDeviceAppDev/AppMarket) | 声明式开发范式 | 本章配套的示例代码,以应用市场首页为例,演示如何使用一多能力适配多设备(或多窗口尺寸)。 |
| [音乐专辑页](https://gitee.com/openharmony/applications_app_samples/tree/master/MultiDeviceAppDev/MusicAlbum) | 声明式开发范式 | 本章配套的示例代码,以音乐专辑页为例,演示如何使用一多能力适配多设备(或多窗口尺寸)。 |
| [蔬菜百科首页](https://gitee.com/openharmony/applications_app_samples/tree/master/MultiDeviceAppDev/Vegetable) | 声明式开发范式 | 一多示例页面,以蔬菜百科首页为例,演示如何使用一多能力适配多设备(或多窗口尺寸)。 |
| [天气](https://gitee.com/openharmony/applications_app_samples/tree/master/MultiDeviceAppDev/Weather) | 声明式开发范式 | 一多示例应用,以天气应用为例,演示如何使用一多能力实现包含多个页面的应用。 |
| [设置应用页面](https://gitee.com/openharmony/applications_app_samples/tree/master/MultiDeviceAppDev/Settings) | 声明式开发范式 | 本章配套的示例代码,以设置应用页面为例,演示如何使用一多能力适配多设备(或多窗口尺寸)。 |
......@@ -29,9 +29,12 @@
| lg | [840, +∞) |
> **说明:**
>
> - 以设备屏幕宽度作为参照物,也可以实现类似的效果。考虑到应用可能以非全屏窗口的形式显示,以应用窗口宽度为参照物更为通用。
>
>
> - 开发者可以根据实际使用场景决定适配哪些断点。如xs断点对应的一般是智能穿戴类设备,如果确定某页面不会在智能穿戴设备上显示,则可以不适配xs断点。
>
> - 可以根据实际需要在lg断点后面新增xl、xxl等断点,但注意新增断点会同时增加UX设计师及应用开发者的工作量,除非必要否则不建议盲目新增断点。
OpenHarmony提供了多种方法,判断应用当前处于何种断点,进而可以调整应用的布局。常见的监听断点变化的方法如下所示:
......@@ -45,69 +48,72 @@ OpenHarmony提供了多种方法,判断应用当前处于何种断点,进而
通过窗口对象监听断点变化的核心是获取窗口对象及注册窗口尺寸变化的回调函数。
1. 在Ability的[onWindowStageCreate](../../application-models/uiability-lifecycle.md)生命周期回调中,获取并记录[窗口](../../reference/apis/js-apis-window.md)对象
1. 在Ability的[onWindowStageCreate](../../application-models/uiability-lifecycle.md)生命周期回调中,通过[窗口](../../reference/apis/js-apis-window.md)对象获取启动时的应用窗口宽度并注册回调函数监听窗口尺寸变化。将窗口尺寸的长度单位[由px换算为vp](../../key-features/multi-device-app-dev/visual-basics.md#视觉基础)后,即可基于前文中介绍的规则得到当前断点值,此时可以使用[状态变量](../../quick-start/arkts-state-mgmt-application-level.md)记录当前的断点值方便后续使用
```ts
// EntryAbility.ts
import Window from '@ohos.window';
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
// ...
onWindowStageCreate(windowStage) {
window.getTopWindow(this.context).then((windowObj) => {
AppStorage.SetOrCreate('windowObj', windowObj)
})
}
// ...
}
```
// MainAbility.ts
import window from '@ohos.window'
import display from '@ohos.display'
2. 在页面中,通过窗口对象获取启动时的应用窗口宽度,同时注册回调函数监听窗口尺寸变化。
```ts
@Entry
@Component
struct Index {
@State private currentBreakpoint: string = 'sm'
export default class MainAbility extends Ability {
private windowObj: window.Window
private curBp: string
...
// 根据当前窗口尺寸更新断点
private updateBreakpoint(windowWidth) {
if (windowWidth < 320) {
this.currentBreakpoint = 'xs'
return
}
if (windowWidth < 520) {
this.currentBreakpoint = 'sm'
return
// 将长度的单位由px换算为vp
let windowWidthVp = windowWidth / (display.getDefaultDisplaySync().densityDPI / 160)
let newBp: string = ''
if (windowWidthVp < 320) {
newBp = 'xs'
} else if (windowWidthVp < 520) {
newBp = 'sm'
} else if (windowWidthVp < 840) {
newBp = 'md'
} else {
newBp = 'lg'
}
if (windowWidth < 840) {
this.currentBreakpoint = 'md'
return
if (this.curBp !== newBp) {
this.curBp = newBp
// 使用状态变量记录当前断点值
AppStorage.SetOrCreate('currentBreakpoint', this.curBp)
}
this.currentBreakpoint = 'lg'
}
aboutToAppear() {
let windowObj: window.Window = AppStorage.Get('windowObj')
// 获取应用启动时的窗口尺寸
windowObj.getProperties().then((windowProperties) => {
this.updateBreakpoint(px2vp(windowProperties.windowRect.width))
})
// 注册回调函数,监听窗口尺寸变化
windowObj.on('windowSizeChange', (data) => {
this.updateBreakpoint(px2vp(data.width))
onWindowStageCreate(windowStage: window.WindowStage) {
windowStage.getMainWindow().then((windowObj) => {
this.windowObj = windowObj
// 获取应用启动时的窗口尺寸
this.updateBreakpoint(windowObj.getWindowProperties().windowRect.width)
// 注册回调函数,监听窗口尺寸变化
windowObj.on('windowSizeChange', (windowSize)=>{
this.updateBreakpoint(windowSize.width)
})
});
...
}
aboutToDisappear() {
let windowObj: window.Window = AppStorage.Get('windowObj')
windowObj.off('windowSizeChange')
// 窗口销毁时,取消窗口尺寸变化监听
onWindowStageDestroy() {
if (this.windowObj) {
this.windowObj.off('windowSizeChange')
}
}
...
}
```
2. 在页面中,获取及使用当前的断点。
```ts
@Entry
@Component
struct Index {
@StorageProp('currentBreakpoint') curBp: string = 'sm'
build() {
Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}) {
Text(this.currentBreakpoint).fontSize(50).fontWeight(FontWeight.Medium)
Text(this.curBp).fontSize(50).fontWeight(FontWeight.Medium)
}
.width('100%')
.height('100%')
......@@ -116,7 +122,7 @@ OpenHarmony提供了多种方法,判断应用当前处于何种断点,进而
```
3. 运行及验证效果。
| | | |
| | | |
| -------- | -------- | -------- |
| ![zh-cn_image_0000001336485520](figures/zh-cn_image_0000001336485520.jpg) | ![zh-cn_image_0000001386645965](figures/zh-cn_image_0000001386645965.jpg) | ![zh-cn_image_0000001386325621](figures/zh-cn_image_0000001386325621.jpg) |
......
# 设置应用页面
本小节以“设置”应用页面为例,介绍如何使用自适应布局能力和响应式布局能力适配不同尺寸窗口。本示例已经在[OpenHarmony应用示例](https://gitee.com/openharmony/applications_app_samples/tree/master/MultiDeviceAppDev/Settings)中开源,读者可以根据需要自行下载源码并运行及查看效果。
## 页面设计
为充分利用屏幕尺寸优势,应用常常有在小屏设备上单栏显示,大屏设备上左右分两栏显示的设计,设置应用页面设计如下。
![](figures/settings_ux.png)
观察“设置”应用页面设计,不同断点下“设置主页”、“WLAN页面”和“更多WLAN设置页面”几乎完全一致,只是在sm断点下采用单栏显示,在md和lg断点下采用双栏显示。
在前面的典型页面场景中,已经介绍了如何分析及实现不同断点下设计相似的单个页面,本小节将展开介绍如何实现不同断点下存在单栏和双栏设计的场景。
为了方便读者理解,本小节将围绕以下三个问题进行介绍。
1. [如何实现单/双栏的显示效果](#如何实现单/双栏的显示效果)
2. [如何实现点击跳转或刷新](#如何实现点击跳转或刷新)
3. [如何实现多级跳转](#如何实现多级跳转)
## 如何实现单/双栏的显示效果
开发者可以使用Row、Column、[RowSplit](../../reference/arkui-ts/ts-container-rowsplit.md)等基础的组件,实现分栏显示的效果,但是需要较多的开发工作量。方舟开发框架在API 9重构了[Navigation组件](../../reference/arkui-ts/ts-basic-components-navigation.md),开发者可以通过配置Navigation组件的属性,控制其按照单栏或双栏的效果进行显示。
Navigation组件由Navbar和Content两部分区域组成,Navigation组件支持Stack、Split以及Auto三种模式。Stack及Split模式下Navigation组件的表现如下图所示。
* Stack模式
![](figures/settings_navigation_stack.png)
* Split模式
![](figures/settings_navigation_split.png)
* Auto模式
Auto模式是指Navigation组件可以根据应用窗口尺寸,自动选择合适的模式:窗口宽度小于520vp时,采用Stack模式显示;窗口宽度大于等于520vp时,采用Split模式显示。当窗口尺寸发生改变时,Navigation组件也会自动在Stack模式和Split模式之间切换。
> **说明:**
>
> * Navigation组件提供的title、navBarWidth、navBarPosition等属性来调整其显示效果。Navigation组件样式的配置与其它组件类似,这里不做赘述。
> * 首次加载Navigation组件所在的页面时,如果Navigation组件处于Split模式,Navigation组件会自动激活其第一个NavRouter孩子节点(后文会展开介绍NavRouter)来刷新Content区域的显示。
设置主页的核心代码如下所示。Navigation组件默认处于Auto模式,其样式会根据应用窗口尺寸在单栏和双栏之间自动切换。
```typescript
@Entry
@Component
struct Index {
build() {
Navigation() {
SettingList()
}
.title($r('app.string.settings'))
.navBarWidth('40%')
.width('100%')
.height('100%')
.backgroundColor($r("sys.color.ohos_id_color_sub_background"))
}
}
```
## 如何实现点击跳转或刷新
Navigation组件通常搭配[NavRouter组件](../../reference/arkui-ts/ts-basic-components-navrouter.md)以及[NavDestination组件](../../reference/arkui-ts/ts-basic-components-navdestination.md)一起使用:
* NavRouter组件用于控制Navigation组件Content区域的显示和刷新逻辑。
* NavDestination组件用于实际刷新Navigation组件Content区域的显示。
### 刷新控制
NavRouter组件用于控制Navigation组件中Content区域的刷新逻辑,其必须包含两个孩子节点。
| | 节点类型 | 节点功能 |
| -------------- | ------------------ | ----------------------------------- |
| 第一个孩子节点 | 容器类组件 | 直接控制NavRouter的显示效果 |
| 第二个孩子节点 | NavDestination组件 | 刷新Navigation组件Content区域的显示 |
NavRouter组件默认提供了点击响应处理,不需要开发者自定义点击事件逻辑。另外,NavRouter组件还提供了onStateChange回调事件,用于通知开发者NavRouter的状态:用户点击NavRouter,激活NavRouter并加载对应的NavDestination子组件时,回调onStateChange(true);NavRouter对应的NavDestination子组件不再显示时,回调onStateChange(false)。
![img](figures/settings_navrouter.png)
结合设置应用的具体场景来看,上图1号小红框是NavRouter的第一个孩子节点,2号红框是NavRouter的第二个孩子节点,相应的核心代码实现如下。
```typescript
@Component
export struct WlanSettingItem {
@LocalStorageLink('selectedLabel') selectedLabel: string = ''
build() {
Column() {
NavRouter() {
MainItem({
title: $r('app.string.wifiTab'),
tag: 'UX',
icon: $r('app.media.wlan'),
label: 'WLAN'
})
NavDestination() {
WlanSetting()
}
.title($r('app.string.wifiTab'))
.backgroundColor($r('sys.color.ohos_id_color_sub_background'))
}
}
}
}
```
### 显示刷新
NavDestination组件用于实际刷新Navigation组件Content区域的显示。激活后的NavRouter对应的NavDestination组件,会占满整个Content区域并刷新其显示。
开发者可以通过NavDestination组件提供的如下属性,调整其最终显示效果:
- backgroundColor:设置NavDestination组件的背景色。
- title:自定义NavDestination组件的标题。
- hideTitleBar:隐藏NavDestination组件的标题栏。
特别的,Navigation组件会根据当前的状态决定是否在NavDestination组件标题栏起始部分自动添加返回键图标。当Navigation组件添加了返回键图标时,还可以自动响应及处理系统三键导航中的返回键事件。
![](figures/settings_navdestination_title.png)
## 如何实现多级跳转
可以在NavDesination组件中,继续使用NavRouter组件,以实现多级跳转。多级跳转场景下,Navigation组件同样会根据当前的状态决定是否自动添加返回键图标及响应系统三键导航中的返回键事件。
| 一级页面 | 二级页面 |
| --------------------------------- | ----------------------------------- |
| ![](figures/settings_primary.png) | ![](figures/settings_secondary.png) |
结合具体场景,红框3是一个NavRouter组件,点击后可以控制Navigation组件中的Content区域刷新为红框4对应的NavDestination组件吗,其核心代码实现如下所示。
```typescript
@Component
export struct WlanMoreSettingItem {
@LocalStorageLink('selectedLabel') selectedLabel: string = ''
build() {
NavRouter() {
SubItemArrow({ title: $r('app.string.moreWlanSettings') })
NavDestination() {
WlanMoreSetting()
}
.title($r('app.string.moreWlanSettings'))
.backgroundColor($r('sys.color.ohos_id_color_sub_background'))
}
}
}
```
## 总结
![](figures/settings_navigation_structure.png)
本示例的基础导航结构上图所示:
* 激活`SettingList`中的`WLANSettingItem`,可以加载及显示`WlanSetting`
* 激活`WlanSetting`中的`WlanMoreSettingItem`,可以加载及显示`WlanMoreSetting`
Navigation组件支持自动切换单栏和双栏的显示效果,同时可以根据当前状态自动添加返回键及响应系统的返回键事件。借助Navigation组件,开发者不用关心单栏和双栏场景的差异而更关注于应用本身,极大的减少开发工作量及提高开发效率。
......@@ -4,18 +4,20 @@
虽然不同应用的页面千变万化,但对其进行拆分和分析,页面中的很多布局场景是相似的。本小节将介绍如何借助自适应布局、响应式布局以及常见的容器类组件,实现应用中的典型布局场景。
| 布局场景 | 实现方案 |
| 布局场景 | 实现方案 |
| -------- | -------- |
| [页签栏](#页签栏) | Tab组件&nbsp;+&nbsp;响应式布局 |
| [运营横幅(Banner)](#运营横幅banner) | Swiper组件&nbsp;+&nbsp;响应式布局 |
| [网格](#网格) | Grid组件&nbsp;/&nbsp;List组件&nbsp;+&nbsp;响应式布局 |
| [侧边栏](#侧边栏) | SiderBar组件&nbsp;+&nbsp;响应式布局 |
| [大图浏览](#大图浏览) | Image组件 |
| [操作入口](#操作入口) | Scroll组件+Row组件横向均分 |
| [顶部](#顶部) | 栅格组件 |
| [缩进布局](#缩进布局) | 栅格组件 |
| [挪移布局](#挪移布局) | 栅格组件 |
| [重复布局](#重复布局) | 栅格组件 |
| [页签栏](#页签栏) | Tab组件&nbsp;+&nbsp;响应式布局 |
| [运营横幅(Banner)](#运营横幅banner) | Swiper组件&nbsp;+&nbsp;响应式布局 |
| [网格](#网格) | Grid组件&nbsp;/&nbsp;List组件&nbsp;+&nbsp;响应式布局 |
| [侧边栏](#侧边栏) | SiderBar组件&nbsp;+&nbsp;响应式布局 |
| [单/双栏](#单/双栏) | Navigation组件&nbsp;+&nbsp;响应式布局 |
| [自定义弹窗](#自定义弹窗) | CustomDialogController组件&nbsp;+&nbsp;响应式布局 |
| [大图浏览](#大图浏览) | Image组件 |
| [操作入口](#操作入口) | Scroll组件+Row组件横向均分 |
| [顶部](#顶部) | 栅格组件 |
| [缩进布局](#缩进布局) | 栅格组件 |
| [挪移布局](#挪移布局) | 栅格组件 |
| [重复布局](#重复布局) | 栅格组件 |
> **说明:**
......@@ -418,6 +420,239 @@ struct SideBarSample {
}
```
## 单/双栏
**布局效果**
| sm | md | lg |
| ------------------------------------------------------------ | ------------------------------------------------ | ------------------------------------------------ |
| 单栏显示,在首页中点击选项可以显示详情。<br>点击详情上方的返回键图标或使用系统返回键可以返回到主页。 | 双栏显示,点击左侧不同的选项可以刷新右侧的显示。 | 双栏显示,点击左侧不同的选项可以刷新右侧的显示。 |
| ![](figures/navigation_sm.png) | ![](figures/navigation_md.png) | ![](figures/navigation_lg.png) |
**实现方案**
单/双栏场景可以使用[Navigation组件](../../reference/arkui-ts/ts-basic-components-navigation.md)实现,Navigation组件可以根据窗口宽度自动切换单/双栏显示,减少开发工作量。
**参考代码**
```
@Component
struct Details {
private imageSrc: Resource
build() {
Column() {
Image(this.imageSrc)
.objectFit(ImageFit.Contain)
.height(300)
.width(300)
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
}
}
@Component
struct Item {
private imageSrc: Resource
private label: string
build() {
NavRouter() {
Text(this.label)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.borderRadius(5)
.backgroundColor('#FFFFFF')
.textAlign(TextAlign.Center)
.width(180)
.height(36)
NavDestination() {
Details({imageSrc: this.imageSrc})
}.title(this.label)
}
}
}
@Entry
@Component
struct NavigationSample {
build() {
Navigation() {
Column({space: 30}) {
Item({label: 'moon', imageSrc: $r('app.media.my_image_moon')})
Item({label: 'sun', imageSrc: $r('app.media.my_image')})
}
.justifyContent(FlexAlign.Center)
.height('100%')
.width('100%')
}
.mode(NavigationMode.Auto)
.backgroundColor('#F1F3F5')
.height('100%')
.width('100%')
.navBarWidth('40%')
.hideToolBar(true)
.title('Sample')
}
}
```
## 自定义弹窗
**布局效果**
| sm | md | lg |
| -------------------------------------------- | --------------------------------------- | --------------------------------------- |
| 弹窗居中显示,<br>与窗口左右两侧各间距24vp。 | 弹窗居中显示,其宽度约为窗口宽度的1/2。 | 弹窗居中显示,其宽度约为窗口宽度的1/3。 |
| ![](figures/custom_dialog_sm.png) | ![](figures/custom_dialog_md.png) | ![](figures/custom_dialog_lg.png) |
**实现方案**
自定义弹窗通常通过[CustomDialogController](../../reference/arkui-ts/ts-methods-custom-dialog-box.md)实现,有两种方式实现本场景的目标效果:
* 通过gridCount属性配置自定义弹窗的宽度。
系统默认对不同断点下的窗口进行了栅格化:sm断点下为4栅格,md断点下为8栅格,lg断点下为12栅格。通过gridCount属性可以配置弹窗占据栅格中的多少列,将该值配置为4即可实现目标效果。
* 将customStyle设置为true,即弹窗的样式完全由开发者自定义。
开发者自定义弹窗样式时,开发者可以根据需要配置弹窗的宽高和背景色(非弹窗区域保持默认的半透明色)。自定义弹窗样式配合[栅格组件](../../reference/arkui-ts/ts-container-gridrow.md)同样可以实现目标效果。
**参考代码**
```
@Entry
@Component
struct CustomDialogSample {
// 通过gridCount配置弹窗的宽度
dialogControllerA: CustomDialogController = new CustomDialogController({
builder: CustomDialogA ({
cancel: this.onCancel,
confirm: this.onConfirm
}),
cancel: this.onCancel,
autoCancel: true,
gridCount: 4,
customStyle: false
})
// 自定义弹窗样式
dialogControllerB: CustomDialogController = new CustomDialogController({
builder: CustomDialogB ({
cancel: this.onCancel,
confirm: this.onConfirm
}),
cancel: this.onCancel,
autoCancel: true,
customStyle: true
})
onCancel() {
console.info('callback when dialog is canceled')
}
onConfirm() {
console.info('callback when dialog is confirmed')
}
build() {
Column() {
Button('CustomDialogA').margin(12)
.onClick(() => {
this.dialogControllerA.open()
})
Button('CustomDialogB').margin(12)
.onClick(() => {
this.dialogControllerB.open()
})
}.width('100%').height('100%').justifyContent(FlexAlign.Center)
}
}
@CustomDialog
struct CustomDialogA {
controller: CustomDialogController
cancel: () => void
confirm: () => void
build() {
Column() {
Text('是否删除此联系人?')
.fontSize(16)
.fontColor('#E6000000')
.margin({bottom: 8, top: 24, left: 24, right: 24})
Row() {
Text('取消')
.fontColor('#007DFF')
.fontSize(16)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.onClick(()=>{
this.controller.close()
this.cancel()
})
Line().width(1).height(24).backgroundColor('#33000000').margin({left: 4, right: 4})
Text('删除')
.fontColor('#FA2A2D')
.fontSize(16)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.onClick(()=>{
this.controller.close()
this.confirm()
})
}.height(40)
.margin({left: 24, right: 24, bottom: 16})
}.borderRadius(24)
}
}
@CustomDialog
struct CustomDialogB {
controller: CustomDialogController
cancel: () => void
confirm: () => void
build() {
GridRow({columns: {sm: 4, md: 8, lg: 12}}) {
GridCol({span: 4, offset: {sm: 0, md: 2, lg: 4}}) {
Column() {
Text('是否删除此联系人?')
.fontSize(16)
.fontColor('#E6000000')
.margin({bottom: 8, top: 24, left: 24, right: 24})
Row() {
Text('取消')
.fontColor('#007DFF')
.fontSize(16)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.onClick(()=>{
this.controller.close()
this.cancel()
})
Line().width(1).height(24).backgroundColor('#33000000').margin({left: 4, right: 4})
Text('删除')
.fontColor('#FA2A2D')
.fontSize(16)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.onClick(()=>{
this.controller.close()
this.confirm()
})
}.height(40)
.margin({left: 24, right: 24, bottom: 16})
}.borderRadius(24).backgroundColor('#FFFFFF')
}
}.margin({left: 24, right: 24})
}
}
```
## 大图浏览
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册