1# Navigation如何实现多场景UI适配
2## 场景介绍
3应用在不同屏幕大小的设备上运行时,往往有不同的UI适配,以聊天应用举例:
4* 在窄屏设备上,联系人和聊天区在多窗口中体现。
5
6* 在宽屏设备上,联系人和聊天区在同一窗口体现。
7
8要做好适配,往往需要开发者开发多套代码,以便运行在不同设备上。但是这样耗时耗力,于是ArkUI针对这种场景提供了分栏组件,可以通过一套代码完成不同设别的适配,本例简单介绍下如何使用分栏组件实现上述场景。
9
10## 效果呈现
11效果图如下所示:
12
13窄屏设备效果图:
14
15![navigation_small](figures/navigation_small.PNG)
16
17宽屏设备效果图:
18
19![navigation_large](figures/navigation_large.PNG)
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