1# 设置应用页面 2 3本小节以“设置”应用页面为例,介绍如何使用自适应布局能力和响应式布局能力适配不同尺寸窗口。 4## 页面设计 5 6为充分利用屏幕尺寸优势,应用常常有在小屏设备上单栏显示,大屏设备上左右分两栏显示的设计,设置应用页面设计如下。 7 8 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  29 30* Split模式 31 32  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 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 385 386## 如何实现多级跳转 387 388可以在NavDesination组件中,继续使用NavRouter组件,以实现多级跳转。多级跳转场景下,Navigation组件同样会根据当前的状态决定是否自动添加返回键图标及响应系统三键导航中的返回键事件。 389 390| 一级页面 | 二级页面 | 391| --------------------------------- | ----------------------------------- | 392|  |  | 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 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-->