1# 安全区域 2 3安全区域是指页面的显示区域,默认不与系统设置的非安全区域比如状态栏、导航栏区域重叠,默认情况下开发者开发的界面都被布局在安全区域内。提供属性方法允许开发者设置组件绘制内容突破安全区域的限制,通过[expandSafeArea](#expandsafearea)属性支持组件不改变布局情况下扩展其绘制区域至安全区外,通过设置[setKeyboardAvoidMode](#setkeyboardavoidmode11)来配置虚拟键盘弹出时页面的避让模式。页面中有标题栏等文字不希望和非安全区重叠时,建议对组件设置expandSafeArea属性达到沉浸式效果,也可以直接通过窗口接口[setWindowLayoutFullScreen](../js-apis-window.md#setwindowlayoutfullscreen9)设置沉浸式。 4 5> **说明:** 6> 7> 从API Version 10开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。<br /> 8> 默认摄像头挖孔区域不为非安全区域,页面不避让挖孔。<br /> 9> 从API Version 12开始,可在module.json5中添加配置项, 摄像头挖孔区域视为非安全区,实现页面默认避让挖孔:<br /> 10 "metadata": [<br /> 11 {<br /> 12 "name": "avoid_cutout",<br /> 13 "value": "true",<br /> 14 }<br /> 15 ],<br /> 16 17 18## expandSafeArea 19 20expandSafeArea(types?: Array<SafeAreaType>, edges?: Array<SafeAreaEdge>) 21 22控制组件扩展其安全区域。 23 24**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 25 26**系统能力:** SystemCapability.ArkUI.ArkUI.Full 27 28**参数:** 29 30| 参数名 | 类型 | 必填 | 说明 | 31| ------ | -------------------------------------------------- | ---- | ------------------------------------------------------------ | 32| types | Array <[SafeAreaType](ts-types.md#safeareatype10)> | 否 | 配置扩展安全区域的类型。未添加[Metadata](../../apis-ability-kit/js-apis-bundleManager-metadata.md)配置项时,页面不避让挖孔, CUTOUT类型不生效。<br />默认值:[SafeAreaType.SYSTEM, SafeAreaType.CUTOUT, SafeAreaType.KEYBOARD] | 33| edges | Array <[SafeAreaEdge](ts-types.md#safeareaedge10)> | 否 | 配置扩展安全区域的方向。<br />默认值:[SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM, SafeAreaEdge.START, SafeAreaEdge.END]<br />扩展至所有非安全区域。 | 34 35> **说明:** 36> 37> 设置expandSafeArea属性进行组件绘制扩展时,建议组件尺寸不要设置固定宽高(百分比除外),当设置固定宽高时,扩展安全区域的方向只支持[SafeAreaEdge.TOP, SafeAreaEdge.START],扩展后的组件尺寸保持不变。 38> 39> 安全区域不会限制内部组件的布局和大小,不会裁剪内部组件。 40> 41> 当父容器是滚动容器时,设置expandSafeArea属性不生效。 42> 43> 设置expandSafeArea()时,不传参,走默认值处理;设置expandSafeArea([],[])时,相当于入参是空数组,此时设置expandSafeArea属性不生效。 44> 45> 组件设置expandSafeArea之后生效的条件为: 46> 1.type为SafeAreaType.KEYBOARD时默认生效,组件不避让键盘。 47> 2.设置其他type,组件的边界与安全区域重合时组件能够延伸到安全区域下。例如:设备顶部状态栏高度100,那么组件在屏幕中的绝对位置需要为0 <= y <= 100。 48> 49> 组件延伸到安全区域下,在安全区域处的事件,如点击事件等可能会被系统拦截,优先给状态栏等系统组件响应。 50> 51> 滚动类容器内的组件不建议设置expandSafeArea属性,如果设置,需要按照组件嵌套关系,将当前节点到滚动类祖先容器间所有直接节点设置expandSafeArea属性,否则expandSafeArea属性在滚动后可能会失效,写法参考[示例7](#示例7滚动类容器扩展安全区)。 52> 53> expandSafeArea属性仅作用于当前组件,不会向父组件或子组件传递,因此使用过程中,所有相关组件均需配置。 54> 55> 在同时设置了expandSafeArea和position属性时,position属性会先生效,expandSafeArea属性会后生效。对于未设置position、offset等绘制属性的组件,如果组件边界没有和避让区重叠,设置expandSafeArea属性不生效,如弹窗和半模态组件。 56> 57> 对于expandSafeArea属性无法生效的场景,若要将组件部署在避让区,需要手动调整组件的坐标。 58 59## setKeyboardAvoidMode<sup>11+</sup> 60 61setKeyboardAvoidMode(value: KeyboardAvoidMode): void 62 63控制虚拟键盘抬起时页面的避让模式。 64 65**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 66 67**系统能力:** SystemCapability.ArkUI.ArkUI.Full 68 69**参数:** 70 71| 参数名 | 类型 | 必填 | 说明 | 72| ------ | ---------------------------------------------------- | ---- | ------------------------------------------------------------ | 73| value | [KeyboardAvoidMode](ts-types.md#keyboardavoidmode11) | 是 | 配置虚拟键盘抬起时页面的避让模式。<br />默认值:KeyboardAvoidMode.OFFSET,键盘抬起时默认页面避让模式为上抬模式。 | 74 75> **说明:** 76> 77> KeyboardAvoidMode的RESIZE模式是压缩Page的大小,Page下设置百分比宽高的组件会跟随Page压缩,直接设置宽高的组件会按设置的固定大小布局。设置KeyboardAvoidMode的RESIZE模式时,expandSafeArea([SafeAreaType.KEYBOARD],[SafeAreaEdge.BOTTOM])不生效。 78> 79> KeyboardAvoidMode.NONE配置Page不避让键盘,Page会被抬起的键盘遮盖。 80 81## getKeyboardAvoidMode 82 83getKeyboardAvoidMode(): KeyboardAvoidMode 84 85返回虚拟键盘抬起时的页面避让模式。 86 87**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 88 89**系统能力:** SystemCapability.ArkUI.ArkUI.Full 90 91**返回值:** 92 93| 名称 | 说明 | 94| ---------------------------------------------------- | ---------------------------------- | 95| [KeyboardAvoidMode](ts-types.md#keyboardavoidmode11) | 返回虚拟键盘抬起时的页面避让模式。 | 96 97## 示例 98 99### 示例1(实现沉浸式效果) 100 101该示例通过设置expandSafeArea属性向顶部和底部扩展安全区实现沉浸式效果。 102 103```ts 104// xxx.ets 105@Entry 106@Component 107struct SafeAreaExample1 { 108 @State text: string = '' 109 controller: TextInputController = new TextInputController() 110 111 build() { 112 Row() { 113 Column() 114 .height('100%').width('100%') 115 .backgroundImage($r('app.media.bg')).backgroundImageSize(ImageSize.Cover) 116 .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) 117 }.height('100%') 118 } 119} 120``` 121 122 123 124### 示例2(同时设置固定宽高和expandSafeArea属性) 125 126该示例展示了同时设置固定宽高和expandSafeArea属性的效果。 127 128```ts 129// xxx.ets 130@Entry 131@Component 132struct SafeAreaExample2 { 133 @State text: string = '' 134 controller: TextInputController = new TextInputController() 135 136 build() { 137 Column() { 138 TextInput({ text: this.text, placeholder: 'input your word...', controller: this.controller }) 139 .placeholderFont({ size: 14, weight: 400 }) 140 .width(320).height(40).offset({y: 120}) 141 .fontSize(14).fontColor(Color.Black) 142 .backgroundColor(Color.White) 143 } 144 .height('780') 145 .width('100%') 146 .backgroundColor('rgb(179,217,235)') 147 .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) 148 } 149} 150``` 151 152如下图:Column组件扩展至了顶部状态栏[SafeAreaEdge.TOP],未扩展至底部导航条[SafeAreaEdge.BOTTOM],扩展后的组件高度与设置的高度一致。 153 154 155 156 157### 示例3(键盘避让时固定背景图位置) 158 159该示例通过为背景图组件设置expandSafeArea属性,来实现拉起键盘进行避让时,背景图保持不动的效果。 160 161```ts 162// xxx.ets 163@Entry 164@Component 165struct SafeAreaExample3 { 166 @State text: string = '' 167 controller: TextInputController = new TextInputController() 168 169 build() { 170 Row() { 171 Stack() { 172 Column() 173 .height('100%').width('100%') 174 .backgroundImage($r('app.media.bg')).backgroundImageSize(ImageSize.Cover) 175 .expandSafeArea([SafeAreaType.KEYBOARD, SafeAreaType.SYSTEM]) 176 Column() { 177 Button('Set caretPosition 1') 178 .onClick(() => { 179 this.controller.caretPosition(1) 180 }) 181 TextInput({ text: this.text, placeholder: 'input your word...', controller: this.controller }) 182 .placeholderFont({ size: 14, weight: 400 }) 183 .width(320).height(40).offset({y: 120}) 184 .fontSize(14).fontColor(Color.Black) 185 .backgroundColor(Color.White) 186 }.width('100%').alignItems(HorizontalAlign.Center) 187 } 188 }.height('100%') 189 } 190} 191``` 192 193 194 195### 示例4(设置键盘避让模式为压缩) 196 197该示例通过调用setKeyboardAvoidMode设置键盘避让模式为RESIZE模式,实现键盘抬起时page的压缩效果。 198 199```ts 200// EntryAbility.ets 201import { KeyboardAvoidMode } from '@kit.ArkUI'; 202 203onWindowStageCreate(windowStage: window.WindowStage) { 204 // Main window is created, set main page for this ability 205 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 206 207 windowStage.loadContent('pages/Index', (err, data) => { 208 let keyboardAvoidMode = windowStage.getMainWindowSync().getUIContext().getKeyboardAvoidMode(); 209 // 设置虚拟键盘抬起时压缩页面大小为减去键盘的高度 210 windowStage.getMainWindowSync().getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.RESIZE); 211 if (err.code) { 212 hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 213 return; 214 } 215 hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); 216 }); 217} 218``` 219 220```ts 221// xxx.ets 222@Entry 223@Component 224struct KeyboardAvoidExample1 { 225 build() { 226 Column() { 227 Row().height("30%").width("100%").backgroundColor(Color.Gray) 228 TextArea().width("100%").borderWidth(1) 229 Text("I can see the bottom of the page").width("100%").textAlign(TextAlign.Center).backgroundColor('rgb(179,217,235)').layoutWeight(1) 230 }.width('100%').height("100%") 231 } 232} 233``` 234 235 236 237### 示例5(设置键盘避让模式为上抬) 238 239该示例通过调用setKeyboardAvoidMode设置键盘避让模式为OFFSET模式,实现键盘抬起时page的上抬效果。但当输入光标距离屏幕底部的高度大于键盘高度时,page不会抬起,如本例中所示。 240 241```ts 242// EntryAbility.ets 243import { KeyboardAvoidMode } from '@kit.ArkUI'; 244 245onWindowStageCreate(windowStage: window.WindowStage) { 246 // Main window is created, set main page for this ability 247 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 248 249 windowStage.loadContent('pages/Index', (err, data) => { 250 let keyboardAvoidMode = windowStage.getMainWindowSync().getUIContext().getKeyboardAvoidMode(); 251 // 设置虚拟键盘抬起时把页面上抬直到露出光标 252 windowStage.getMainWindowSync().getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.OFFSET); 253 if (err.code) { 254 hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 255 return; 256 } 257 hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); 258 }); 259} 260``` 261 262```ts 263// xxx.ets 264@Entry 265@Component 266struct KeyboardAvoidExample2 { 267 build() { 268 Column() { 269 Row().height("30%").width("100%").backgroundColor(Color.Gray) 270 TextArea().width("100%").borderWidth(1) 271 Text("I can see the bottom of the page").width("100%").textAlign(TextAlign.Center).backgroundColor('rgb(179,217,235)').layoutWeight(1) 272 }.width('100%').height("100%") 273 } 274} 275``` 276 277 278 279### 示例6(切换避让模式) 280 281该示例通过调用setKeyboardAvoidMode来实现OFFSET、RESIZE和NONE模式之间的切换,实现三种不同的键盘避让效果。 282 283```ts 284import { hilog } from '@kit.PerformanceAnalysisKit'; 285import { KeyboardAvoidMode } from '@kit.ArkUI'; 286@Entry 287@Component 288 289struct KeyboardAvoidExample3 { 290 build() { 291 Column() { 292 Row({space:15}) { 293 Button('OFFSET') 294 .onClick(() => { 295 this.getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.OFFSET); 296 hilog.info(0x0000, 'keyboardAvoidMode: %{public}s', JSON.stringify(this.getUIContext().getKeyboardAvoidMode())); 297 }) 298 .layoutWeight(1) 299 Button('RESIZE') 300 .onClick(() => { 301 this.getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.RESIZE); 302 hilog.info(0x0000, 'keyboardAvoidMode: %{public}s', JSON.stringify(this.getUIContext().getKeyboardAvoidMode())); 303 }) 304 .layoutWeight(1) 305 Button('NONE') 306 .onClick(() => { 307 this.getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.NONE); 308 hilog.info(0x0000, 'keyboardAvoidMode: %{public}s', JSON.stringify(this.getUIContext().getKeyboardAvoidMode())); 309 }) 310 .layoutWeight(1) 311 } 312 .height("30%") 313 .width("100%") 314 .backgroundColor(Color.Gray) 315 316 TextArea() 317 .width("100%") 318 .borderWidth(1) 319 320 Text("I can see the bottom of the page") 321 .width("100%") 322 .textAlign(TextAlign.Center) 323 .backgroundColor('rgb(179,217,235)') 324 .layoutWeight(1) 325 326 TextArea() 327 .width("100%") 328 .borderWidth(1) 329 } 330 .width('100%') 331 .height("100%") 332 } 333} 334``` 335OFFSET模式 336 337 338 339RESIZE模式 340 341 342 343NONE模式 344 345 346 347### 示例7(滚动类容器扩展安全区) 348 349该示例通过在滚动类容器内调用expandSafeArea属性实现沉浸式效果。 350 351```ts 352class SwiperDataSource implements IDataSource { 353 private list: Array<Color> = [] 354 constructor(list: Array<Color>) { 355 this.list = list 356 } 357 totalCount(): number { 358 return this.list.length 359 } 360 getData(index: number): Color { 361 return this.list[index] 362 } 363 registerDataChangeListener(listener: DataChangeListener): void { 364 } 365 unregisterDataChangeListener(listener: DataChangeListener): void { 366 } 367} 368@Entry 369@Component 370struct ExpandSafeAreaTest { 371 private swiperController: SwiperController = new SwiperController() 372 private swiperData: SwiperDataSource = new SwiperDataSource([]) 373 private list: Array<Color> = [ 374 Color.Pink, 375 Color.Blue, 376 Color.Green 377 ] 378 aboutToAppear(): void { 379 this.swiperData = new SwiperDataSource(this.list) 380 } 381 build() { 382 Scroll() { 383 Column() { 384 Swiper(this.swiperController) { 385 LazyForEach(this.swiperData, (item: Color, index: number) => { 386 Column() { 387 Text('banner' + index).fontSize(50).fontColor(Color.White) 388 } 389 .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) 390 .width('100%') 391 .height(400) 392 .backgroundColor(item) 393 }) 394 } 395 .loop(true) 396 .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) 397 .clip(false) 398 Column(){ 399 Text("Tab页Content").fontSize(50) 400 }.width("100%").height(1000) 401 .backgroundColor(Color.Grey) 402 }.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) 403 } 404 .clip(false) 405 .edgeEffect(EdgeEffect.None) 406 .width("100%").height("100%") 407 } 408} 409``` 410