1# Navigation如何实现多场景UI适配 2## 场景介绍 3应用在不同屏幕大小的设备上运行时,往往有不同的UI适配,以聊天应用举例: 4* 在窄屏设备上,联系人和聊天区在多窗口中体现。 5 6* 在宽屏设备上,联系人和聊天区在同一窗口体现。 7 8要做好适配,往往需要开发者开发多套代码,以便运行在不同设备上。但是这样耗时耗力,于是ArkUI针对这种场景提供了分栏组件,可以通过一套代码完成不同设别的适配,本例简单介绍下如何使用分栏组件实现上述场景。 9 10## 效果呈现 11效果图如下所示: 12 13窄屏设备效果图: 14 15 16 17宽屏设备效果图: 18 19 20## 运行环境 21本例基于以下环境开发,开发者也可以基于其它适配的版本进行开发: 22- IDE:DevEco Studio 3.1 Release 23- SDK: Ohos_sdk_public 3.2.12.5(API Version 9 Release) 24## 实现思路 25想要实现一多效果,所有的页面元素必须在Navigation的容器中展示,Navigation一般作为页面的根容器,包括单页面、分栏和自适应三种显示模式,可通过mode属性设置页面的显示模式。 26 27导航区中使用NavRouter子组件实现导航栏功能,内容页主要显示NavDestination子组件中的内容。 28 29NavRouter是和Navigation搭配使用的特殊子组件,默认提供点击响应处理,不需要开发者自定义点击事件逻辑。NavRouter有且仅有两个根节点,第二个根节点是NavDestination。NavDestination用于显示Navigation组件的内容页。当开发者点击NavRouter组件时,会跳转到对应的NavDestination内容区。 30 31 32本例涉及一些关键特性以及实现方法如下: 33- 创建Navigation组件,同时通过设置mode属性为auto来控制页面显示效果。 34- Navigation通过与NavRouter组件搭配使用,实现页面分栏效果。 35 36 > NavRouter必须包含两个子组件,其子组件即为实现分栏效果的组件,其中第二个子组件必须为NavDestination(第一个即可理解为为导航栏,第二个组件可理解为内容区)。 37 38- 通过向父组件NavRouter添加子组件NavDestination,创建导航内容区并添加文本。 39- 内容区域的补充:根据应用的场景,添加TextArea组件完善内容区。 40 41 42## 开发步骤 431. 创建Navigation组件,同时通过设置mode属性为auto来控制页面显示(自适应模式下,当设备宽度大于520vp时,Navigation组件采用分栏模式,反之采用单页面模式)。 44具体代码如下: 45 ```ts 46 build() { 47 Column() { 48 Navigation() { 49 ... 50 } 51 // Navigation组件mode属性设置为auto。自适应模式下,当设备宽度大于520vp时,Navigation组件采用分栏模式,反之采用单页面模式。 52 .mode(NavigationMode.Auto) 53 } 54 .height('100%') 55 } 56 ``` 572. 通过NavRouter组件创建导航栏:Navigation通过与NavRouter组件搭配实现页面分栏效果。 58 * 自定义导航栏NavigationTitle。 59 * 添加Navigation子组件NavRoute,创建导航栏。 60 * 通过ForEach循环渲染导航栏内容,且导航栏内容通过List组件显示。 61 具体代码如下: 62 ```ts 63 // 自定义导航栏title 64 @Builder NavigationTitle(index) { 65 Column() { 66 Row() { 67 Text('互动交流' + index + '群') 68 .fontColor('#182431') 69 .fontSize(20) 70 } 71 } 72 .width($r("app.float.titHeightFloat")) 73 } 74 75 build() { 76 Column() { 77 Navigation() { 78 Text('联系人(' + this.arr.length + ')') 79 .fontWeight(500) 80 .margin({ top: 10, right: 10, left: 19 }) 81 .fontSize(17) 82 83 List({ initialIndex: 0 }) { 84 // 通过ForEach循环渲染导航栏内容 85 ForEach(this.arr, (item: number, index: number) => { 86 ListItem() { 87 // 导航组件,默认提供点击响应处理 88 NavRouter() { 89 // 导航区内容 90 Column() { 91 Row() { 92 Image($r('app.media.icon1')) 93 .width(35) 94 .height(35) 95 .borderRadius(35) 96 .margin({ left: 3, right: 10 }) 97 Text('互动交流' + item + '群') 98 .fontSize(22) 99 .textAlign(TextAlign.Center) 100 } 101 .padding({ left: 10 }) 102 .width('100%') 103 .height(80) 104 .backgroundColor(this.dex == index ? '#eee' : '#fff') 105 106 Divider().strokeWidth(1).color('#F1F3F5') 107 }.width('100%') 108 109 ... 110 111 } 112 .width('100%') 113 } 114 }, item => item) 115 } 116 .height('100%').margin({ top: 12 }) 117 } 118 // Navigation组件默认为自适应模式,此时mode属性为NavigationMode.Auto。自适应模式下,当设备宽度大于520vp时,Navigation组件采用分栏模式,反之采用单页面模式。 119 .mode(NavigationMode.Auto) 120 .hideTitleBar(true) 121 .hideToolBar(true) 122 } 123 .height('100%') 124 } 125 ``` 126 1273. 通过添加组件NavDestination,创建内容栏并添加文本。 128 NavRouter包含两个子组件,其子组件即为实现分栏效果的组件,其中第二个子组件必须为NavDestination,用于显示导航内容区(第一个即可理解为为导航栏,第二个组件可理解为内容区); 129 内容区部分代码: 130 131 ```ts 132 build() { 133 Column() { 134 Navigation() { 135 ... 136 137 // 导航组件,默认提供点击响应处理 138 NavRouter() { 139 // 导航区内容 140 ... 141 142 // NavRouter组件的子组件,用于显示导航内容区。 143 NavDestination() { 144 // 内容区 145 ForEach([0, 1], (item: number) => { 146 Flex({ direction: FlexDirection.Row }) { 147 Row() { 148 Image($r('app.media.icon2')) 149 .width(40) 150 .height(40) 151 .borderRadius(40) 152 .margin({ right: 15 }) 153 Text('今天幸运数字' + index.toString()) 154 .fontSize(20) 155 .height(40) 156 .backgroundColor('#f1f9ff') 157 .borderRadius(10) 158 .padding(10) 159 } 160 .padding({ left: 15 }) 161 .margin({ top: 15 }) 162 } 163 }, item => item) 164 .... 165 } 166 167 // 设置内容区标题 168 .title(this.NavigationTitle(index)) 169 } 170 } 171 // Navigation组件默认为自适应模式,此时mode属性为NavigationMode.Auto。自适应模式下,当设备宽度大于520vp时,Navigation组件采用分栏模式,反之采用单页面模式。 172 .mode(NavigationMode.Auto) 173 .hideTitleBar(true) 174 .hideToolBar(true) 175 } 176 .height('100%') 177 } 178 ``` 179 180 1814. 内容区域的补充:完善内容区域文本组件。 182具体代码块如下: 183 184 ```ts 185 ... 186 Column() { 187 TextArea({ 188 placeholder: '请输入文字', 189 }) 190 .placeholderFont({ size: 16, weight: 400 }) 191 .width('100%') 192 .height($r("app.float.heightFloat")) 193 .fontSize(16) 194 .fontColor('#182431') 195 .backgroundColor('#FFFFFF') 196 .borderRadius(0) 197 } 198 .margin({ top: $r("app.float.marHeightFloat") }) 199 .height($r("app.float.ColHeightFloat")) 200 .justifyContent(FlexAlign.End) 201 ... 202 ``` 203 204## 完整代码 205示例完整代码如下: 206 207```ts 208@Entry 209@Component 210struct NavigationExample { 211 @State arr: number[] = [0, 1, 2, 3, 4, 5] 212 @State dex: number = 0 213 214 @Builder NavigationTitle(index) { 215 Column() { 216 Row() { 217 Text('互动交流' + index + '群') 218 .fontColor('#182431') 219 .fontSize(20) 220 } 221 } 222 .width($r("app.float.titHeightFloat")) 223 } 224 225 build() { 226 Column() { 227 Navigation() { 228 Text('联系人(' + this.arr.length + ')') 229 .fontWeight(500) 230 .margin({ top: 10, right: 10, left: 19 }) 231 .fontSize(17) 232 List({ initialIndex: 0 }) { 233 // 通过ForEach循环渲染导航栏内容 234 ForEach(this.arr, (item: number, index: number) => { 235 ListItem() { 236 // 导航组件,默认提供点击响应处理 237 NavRouter() { 238 // 导航区内容 239 Column() { 240 Row() { 241 Image($r('app.media.icon1')) 242 .width(35) 243 .height(35) 244 .borderRadius(35) 245 .margin({ left: 3, right: 10 }) 246 Text('互动交流' + item + '群') 247 .fontSize(22) 248 .textAlign(TextAlign.Center) 249 } 250 .padding({ left: 10 }) 251 .width('100%') 252 .height(80) 253 .backgroundColor(this.dex == index ? '#eee' : '#fff') 254 255 Divider().strokeWidth(1).color('#F1F3F5') 256 }.width('100%') 257 258 // NavRouter组件的子组件,用于显示导航内容区。 259 NavDestination() { 260 ForEach([0, 1], (item: number) => { 261 Flex({ direction: FlexDirection.Row }) { 262 Row() { 263 Image($r('app.media.icon2')) 264 .width(40) 265 .height(40) 266 .borderRadius(40) 267 .margin({ right: 15 }) 268 Text('今天幸运数字' + index.toString()) 269 .fontSize(20) 270 .height(40) 271 .backgroundColor('#f1f9ff') 272 .borderRadius(10) 273 .padding(10) 274 } 275 .padding({ left: 15 }) 276 .margin({ top: 15 }) 277 } 278 }, item => item) 279 280 Row() { 281 Text('幸运数字' + item.toString()) 282 .fontSize(20) 283 .margin({ right: 10 }) 284 .height(40) 285 .backgroundColor('#68c059') 286 .borderRadius(10) 287 .padding(10) 288 Image($r('app.media.icon3')) 289 .width(40) 290 .height(40) 291 .borderRadius(40) 292 .margin({ right: 15 }) 293 } 294 .padding({ left: 15 }) 295 .margin({ top: 150 }) 296 .width('100%') 297 .direction(Direction.Rtl) 298 299 Column() { 300 TextArea({placeholder: '请输入文字',}) 301 .placeholderFont({ size: 16, weight: 400 }) 302 .width('100%') 303 .height($r("app.float.heightFloat")) 304 .fontSize(16) 305 .fontColor('#182431') 306 .backgroundColor('#FFFFFF') 307 .borderRadius(0) 308 } 309 .margin({ top: $r("app.float.marHeightFloat") }) 310 .height($r("app.float.ColHeightFloat")) 311 .justifyContent(FlexAlign.End) 312 } 313 .backgroundColor('#eee') 314 // 设置内容区标题 315 .title(this.NavigationTitle(index)) 316 } 317 .width('100%') 318 } 319 }, item => item) 320 } 321 .height('100%').margin({ top: 12 }) 322 } 323 // Navigation组件mode属性设置为auto。自适应模式下,当设备宽度大于520vp时,Navigation组件采用分栏模式,反之采用单页面模式。 324 .mode(NavigationMode.Auto) 325 .hideTitleBar(true) 326 .hideToolBar(true) 327 } 328 .height('100%') 329 } 330} 331``` 332## 参考 333[List组件](../application-dev/reference/apis-arkui/arkui-ts/ts-container-list.md) 334 335[Flex组件](../application-dev/reference/apis-arkui/arkui-ts/ts-container-flex.md) 336 337[Navigation](../application-dev/reference/apis-arkui/arkui-ts/ts-basic-components-navigation.md) 338 339[NavRouter](../application-dev/reference/apis-arkui/arkui-ts/ts-basic-components-navrouter.md) 340 341[NavDestination](../application-dev/reference/apis-arkui/arkui-ts/ts-basic-components-navdestination.md) 342 343