1# 设置应用页面
2
3本小节以“设置”应用页面为例,介绍如何使用自适应布局能力和响应式布局能力适配不同尺寸窗口。
4## 页面设计
5
6为充分利用屏幕尺寸优势,应用常常有在小屏设备上单栏显示,大屏设备上左右分两栏显示的设计,设置应用页面设计如下。
7
8![](figures/settings_ux.png)
9
10观察“设置”应用页面设计,不同断点下“设置主页”、“WLAN页面”和“更多WLAN设置页面”几乎完全一致,只是在sm断点下采用单栏显示,在md和lg断点下采用双栏显示。
11
12在前面的典型页面场景中,已经介绍了如何分析及实现不同断点下设计相似的单个页面,本小节将展开介绍如何实现不同断点下存在单栏和双栏设计的场景。
13
14为了方便读者理解,本小节将围绕以下三个问题进行介绍。
15
161. [如何实现单/双栏的显示效果](#如何实现单/双栏的显示效果)
172. [如何实现点击跳转或刷新](#如何实现点击跳转或刷新)
183. [如何实现多级跳转](#如何实现多级跳转)
19
20## 如何实现单/双栏的显示效果
21
22开发者可以使用Row、Column、[RowSplit](../../reference/apis-arkui/arkui-ts/ts-container-rowsplit.md)等基础的组件,实现分栏显示的效果,但是需要较多的开发工作量。方舟开发框架在API 9重构了[Navigation组件](../../reference/apis-arkui/arkui-ts/ts-basic-components-navigation.md),开发者可以通过配置Navigation组件的属性,控制其按照单栏或双栏的效果进行显示。
23
24Navigation组件由NavBar和Content两部分区域组成,Navigation组件支持Stack、Split以及Auto三种模式。Stack及Split模式下Navigation组件的表现如下图所示。
25
26* Stack模式
27
28  ![](figures/settings_navigation_stack.png)
29
30* Split模式
31
32  ![](figures/settings_navigation_split.png)
33
34* Auto模式
35
36  Auto模式是指Navigation组件可以根据应用窗口尺寸,自动选择合适的模式:窗口宽度小于520vp时,采用Stack模式显示;窗口宽度大于等于520vp时,采用Split模式显示。当窗口尺寸发生改变时,Navigation组件也会自动在Stack模式和Split模式之间切换。
37
38> **说明:**
39>
40> * Navigation组件提供的title、navBarWidth、navBarPosition等属性来调整其显示效果。Navigation组件样式的配置与其它组件类似,这里不做赘述。
41> * 首次加载Navigation组件所在的页面时,如果Navigation组件处于Split模式,Navigation组件会自动激活其第一个NavRouter子节点(后文会展开介绍NavRouter)来刷新Content区域的显示。
42
43设置主页的核心代码如下所示。Navigation组件默认处于Auto模式,其样式会根据应用窗口尺寸在单栏和双栏之间自动切换。
44
45```ts
46import { SettingList } from '@ohos/settingItems';
47
48@Entry
49@Component
50struct Index {
51  build() {
52    Navigation() {
53      SettingList()
54    }
55    .title($r('app.string.settings'))
56    .navBarWidth('40%')
57    .width('100%')
58    .height('100%')
59    .backgroundColor($r("sys.color.ohos_id_color_sub_background"))
60  }
61}
62```
63```ts
64//核心代码 SettingList.ets
65import { MainItem } from '../components/MainItem';
66import { ItemGroup } from '../components/ItemGroup';
67import { SearchBox } from '../components/SearchBox';
68import { MoreConnectionsItem } from '../moreconnections/MoreConnectionsItem';
69import { WlanSettingItem } from '../wlan/WlanSettingItem';
70
71class  ItemObj {
72  title?: Resource
73  tag?: Resource
74  icon?:Resource
75}
76let bluetoothTab:ItemObj={
77  title: $r('app.string.bluetoothTab'),
78  tag: $r('app.string.enabled'),
79  icon: $r('app.media.blueTooth'),
80}
81let mobileData:ItemObj={
82  title: $r('app.string.mobileData'),
83  icon: $r('app.media.mobileData'),
84}
85let brightnessTab:ItemObj={
86  title: $r('app.string.brightnessTab'),
87  icon: $r('app.media.displayAndBrightness'),
88}
89let volumeControlTab:ItemObj={
90  title: $r('app.string.volumeControlTab'),
91  icon: $r('app.media.volume'),
92}
93let biometricsAndPassword:ItemObj={
94  title: $r('app.string.biometricsAndPassword'),
95  icon: $r('app.media.biometricsAndPassword'),
96}
97let applyTab:ItemObj={
98  title: $r('app.string.applyTab'),
99  icon: $r('app.media.application'),
100}
101let storageTab:ItemObj={
102  title: $r('app.string.storageTab'),
103  icon: $r('app.media.storage'),
104}
105let security:ItemObj={
106  title: $r('app.string.security'),
107  icon: $r('app.media.security'),
108}
109let privacy:ItemObj={
110  title: $r('app.string.privacy'),
111  icon: $r('app.media.privacy'),
112}
113let usersAccountsTab:ItemObj={
114  title: $r('app.string.usersAccountsTab'),
115  icon: $r('app.media.userAccounts'),
116}
117let systemTab:ItemObj={
118  title: $r('app.string.systemTab'),
119  icon: $r('app.media.system'),
120}
121let aboutTab:ItemObj={
122  title: $r('app.string.aboutTab'),
123  icon: $r('app.media.aboutDevice'),
124}
125
126@Component
127export struct SettingList {
128  @Builder
129  CustomDivider() {
130    Divider()
131      .strokeWidth('1px')
132      .color($r('sys.color.ohos_id_color_list_separator'))
133      .margin({ left: 48, right: 8 })
134  }
135
136   build() {
137     List({ space: 12 }) {
138       ListItem() {
139        SearchBox()
140       }
141       .padding({ top: 8, bottom: 8 })
142       .width('100%')
143
144      ListItem() {
145        ItemGroup() {
146          WlanSettingItem()
147
148          this.CustomDivider()
149
150          MainItem(bluetoothTab)
151
152          this.CustomDivider()
153
154          MainItem(mobileData)
155
156          this.CustomDivider()
157          MoreConnectionsItem()
158        }
159      }
160
161      ListItem() {
162        ItemGroup() {
163          MainItem(brightnessTab)
164        }
165      }
166
167      ListItem() {
168        ItemGroup() {
169          MainItem(volumeControlTab)
170        }
171      }
172
173      ListItem() {
174        ItemGroup() {
175          MainItem(biometricsAndPassword)
176
177          this.CustomDivider()
178
179          MainItem(applyTab)
180
181          this.CustomDivider()
182
183          MainItem(storageTab)
184
185          this.CustomDivider()
186
187          MainItem(security)
188
189          this.CustomDivider()
190
191          MainItem(privacy)
192        }
193      }
194
195      ListItem() {
196        ItemGroup() {
197          MainItem(usersAccountsTab)
198
199          this.CustomDivider()
200
201          MainItem(systemTab)
202
203          this.CustomDivider()
204
205          MainItem(aboutTab)
206        }
207      }
208
209    }
210    .padding({ left: 12, right: 12 })
211    .width('100%')
212    .height('100%')
213    .backgroundColor($r('sys.color.ohos_id_color_sub_background'))
214   }
215}
216
217
218```
219## 如何实现点击跳转或刷新
220
221Navigation组件通常搭配[NavRouter组件](../../reference/apis-arkui/arkui-ts/ts-basic-components-navrouter.md)以及[NavDestination组件](../../reference/apis-arkui/arkui-ts/ts-basic-components-navdestination.md)一起使用:
222
223* NavRouter组件用于控制Navigation组件Content区域的显示和刷新逻辑。
224* NavDestination组件用于实际刷新Navigation组件Content区域的显示。
225
226### 刷新控制
227
228NavRouter组件用于控制Navigation组件中Content区域的刷新逻辑,其必须包含两个子节点。
229
230|                | 节点类型           | 节点功能                            |
231| -------------- | ------------------ | ----------------------------------- |
232| 第一个子节点 | 容器类组件         | 直接控制NavRouter的显示效果         |
233| 第二个子节点 | NavDestination组件 | 刷新Navigation组件Content区域的显示 |
234
235NavRouter组件默认提供了点击响应处理,不需要开发者自定义点击事件逻辑。另外,NavRouter组件还提供了onStateChange回调事件,用于通知开发者NavRouter的状态:用户点击NavRouter,激活NavRouter并加载对应的NavDestination子组件时,回调onStateChange(true);NavRouter对应的NavDestination子组件不再显示时,回调onStateChange(false)。
236
237![img](figures/settings_navrouter.png)
238
239结合设置应用的具体场景来看,上图1号小红框是NavRouter的第一个子节点,2号红框是NavRouter的第二个子节点,相应的核心代码实现如下。
240
241```ts
242import { MainItem } from '../components/MainItem';
243import { WlanMoreSettingItem } from '../components/WlanMoreSettingItem';
244import { SubItemToggle } from '../components/SubItemToggle';
245import { SubItemWifi } from '../components/SubItemWifi';
246import { ItemDescription } from '../components/ItemDescription';
247import { ItemGroup } from '../components/ItemGroup';
248
249class  MainItemObj {
250  title?: Resource
251  tag?: string
252  icon?:Resource
253  label?: string
254}
255let mainItem:MainItemObj={
256  title: $r('app.string.wifiTab'),
257  tag: 'UX',
258  icon: $r('app.media.wlan'),
259  label: 'WLAN'
260}
261@Component
262export struct WlanSettingItem {
263  @LocalStorageLink('selectedLabel') selectedLabel: string  = ''
264
265  build() {
266    Column() {
267      NavRouter() {
268        MainItem(mainItem)
269
270        NavDestination() {
271          WlanSetting()
272        }
273        .title($r('app.string.wifiTab'))
274        .backgroundColor($r('sys.color.ohos_id_color_sub_background'))
275      }
276    }
277  }
278}
279@Component
280struct WlanSetting {
281  @Builder CustomDivider() {
282    Divider()
283      .strokeWidth('1px')
284      .color($r('sys.color.ohos_id_color_list_separator'))
285      .margin({left: 12, right: 8})
286  }
287
288  build() {
289    Column() {
290      Column() {
291        ItemGroup() {
292          SubItemToggle({title: $r('app.string.wifiTab'), isOn: true})
293        }
294
295        Row().height(16)
296
297        ItemGroup() {
298          WlanMoreSettingItem()
299        }
300      }
301      .margin({bottom: 19.5})
302      .flexShrink(0)
303
304      Scroll() {
305        Column() {
306          ItemDescription({description: $r('app.string.wifiTipConnectedWLAN')})
307            .padding({
308              left: 12,
309              right: 12,
310              bottom: 9.5
311            })
312
313          ItemGroup() {
314            SubItemWifi({
315              title: 'UX',
316              subTitle: $r('app.string.wifiSummaryConnected'),
317              isConnected: true,
318              icon: $r('app.media.ic_wifi_signal_4_dark')
319            })
320          }
321
322          Column() {
323            ItemDescription({description: $r('app.string.wifiTipValidWLAN')})
324              .margin({
325                left: 12,
326                right: 12,
327                top: 19.5,
328                bottom: 9.5
329              })
330
331            ItemGroup() {
332              SubItemWifi({
333                title: 'Huwe-yee',
334                subTitle: $r('app.string.wifiSummaryEncrypted'),
335                isConnected: false,
336                icon: $r('app.media.ic_wifi_lock_signal_4_dark')
337              })
338
339              this.CustomDivider()
340
341              SubItemWifi({
342                title: 'UX-5G',
343                subTitle: $r('app.string.wifiSummaryOpen'),
344                isConnected: false,
345                icon: $r('app.media.ic_wifi_signal_4_dark')
346              })
347
348              this.CustomDivider()
349
350              SubItemWifi({
351                title: 'E1-AP',
352                subTitle: $r('app.string.wifiSummarySaveOpen'),
353                isConnected: false,
354                icon: $r('app.media.ic_wifi_signal_4_dark')
355              })
356            }
357          }
358        }
359      }
360      .scrollable(ScrollDirection.Vertical)
361      .scrollBar(BarState.Off)
362      .width('100%')
363      .flexShrink(1)
364    }
365    .width('100%')
366    .height('100%')
367    .padding({left: 12, right: 12})
368  }
369}
370```
371
372### 显示刷新
373
374NavDestination组件用于实际刷新Navigation组件Content区域的显示。激活后的NavRouter对应的NavDestination组件,会占满整个Content区域并刷新其显示。
375
376开发者可以通过NavDestination组件提供的如下属性,调整其最终显示效果:
377
378- backgroundColor:设置NavDestination组件的背景色。
379- title:自定义NavDestination组件的标题。
380- hideTitleBar:隐藏NavDestination组件的标题栏。
381
382特别的,Navigation组件会根据当前的状态决定是否在NavDestination组件标题栏起始部分自动添加返回键图标。当Navigation组件添加了返回键图标时,还可以自动响应及处理系统三键导航中的返回键事件。
383
384![](figures/settings_navdestination_title.png)
385
386## 如何实现多级跳转
387
388可以在NavDesination组件中,继续使用NavRouter组件,以实现多级跳转。多级跳转场景下,Navigation组件同样会根据当前的状态决定是否自动添加返回键图标及响应系统三键导航中的返回键事件。
389
390| 一级页面                          | 二级页面                            |
391| --------------------------------- | ----------------------------------- |
392| ![](figures/settings_primary.png) | ![](figures/settings_secondary.png) |
393
394
395结合具体场景,红框3是一个NavRouter组件,点击后可以控制Navigation组件中的Content区域刷新为红框4对应的NavDestination组件吗,其核心代码实现如下所示。
396
397```ts
398
399import { SubItemArrow } from '../components/SubItemArrow';//组件请参考相关示例
400import { SubItemToggle } from '../components/SubItemToggle';
401import { ItemGroup } from '../components/ItemGroup';
402import { ItemDescription } from '../components/ItemDescription';
403
404class SubItemArrowObj{
405  title?: Resource
406}
407let subItemArrow:SubItemArrowObj={
408  title: $r('app.string.moreWlanSettings')
409}
410@Component
411export struct WlanMoreSettingItem {
412  @LocalStorageLink('selectedLabel') selectedLabel: string = ''
413
414  build() {
415    NavRouter() {
416      SubItemArrow(subItemArrow)
417
418      NavDestination() {
419        WlanMoreSetting()
420      }
421      .title($r('app.string.moreWlanSettings'))
422      .backgroundColor($r('sys.color.ohos_id_color_sub_background'))
423    }
424  }
425}
426
427@Component
428export struct WlanMoreSetting {
429  build() {
430    Scroll() {
431      Column() {
432        ItemGroup() {
433          SubItemArrow({
434            title: $r('app.string.wlanPlus'),
435            tag: $r('app.string.enabled')
436          })
437        }
438        ItemDescription({description: $r('app.string.wlanPlusTip')})
439          .margin({
440            top: 8,
441            bottom: 24,
442            left: 12,
443            right: 12
444          })
445
446        ItemGroup() {
447          SubItemArrow({ title: $r('app.string.wlanDirect') })
448        }
449
450        Blank().height(12)
451
452        ItemGroup() {
453          SubItemToggle({title: $r('app.string.wlanSecurityCheck')})
454        }
455
456        ItemDescription({description: $r('app.string.wlanSecurityCheckTip')})
457          .margin({
458            top: 8,
459            bottom: 24,
460            left: 12,
461            right: 12
462          })
463
464        ItemGroup() {
465          SubItemArrow({title: $r('app.string.savedWlan')})
466          Divider()
467            .strokeWidth('1px')
468            .color($r('sys.color.ohos_id_color_list_separator'))
469            .margin({left: 12, right: 8})
470          SubItemArrow({title: $r('app.string.installCertificates')})
471        }
472      }
473      .backgroundColor($r('sys.color.ohos_id_color_sub_background'))
474      .padding({left: 12, right: 12})
475    }
476    .scrollBar(BarState.Off)
477    .width('100%')
478  }
479}
480```
481
482## 总结
483
484![](figures/settings_navigation_structure.png)
485
486本示例的基础导航结构上图所示:
487
488* 激活`SettingList`中的`WLANSettingItem`,可以加载及显示`WlanSetting`。
489* 激活`WlanSetting`中的`WlanMoreSettingItem`,可以加载及显示`WlanMoreSetting`。
490
491Navigation组件支持自动切换单栏和双栏的显示效果,同时可以根据当前状态自动添加返回键及响应系统的返回键事件。借助Navigation组件,开发者不用关心单栏和双栏场景的差异而更关注于应用本身,极大的减少开发工作量及提高开发效率。
492
493## 相关实例
494
495针对“设置”应用页面,有以下相关实例可以参考:
496
497- [典型页面场景:设置应用页面(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/MultiDeviceAppDev/Settings)
498
499<!--RP1-->
500<!--RP1End-->