文章目录
一、安全区域
安全区域定义为页面的显示区域,其默认不与系统设置的非安全区域(如状态栏、导航栏)重叠,以确保开发者设计的界面均布局于安全区域内。然而,当Web组件启用沉浸式模式时,网页元素可能会出现与状态栏或导航栏重叠的问题。具体示例如图1所示,中间部分的区域即为安全区域,而顶部状态栏、屏幕挖孔区域和底部导航条则被界定为避让区,Web组件开启沉浸式效果时,网页内底部元素与导航条发生重叠。
提供属性方法允许开发者设置组件绘制内容突破安全区域的限制,通过expandSafeArea属性支持组件不改变布局情况下扩展其绘制区域至安全区外,通过设置setKeyboardAvoidMode来配置虚拟键盘弹出时页面的避让模式。页面中有标题栏等文字不希望和非安全区重叠时,建议对组件设置expandSafeArea属性达到沉浸式效果,也可以直接通过窗口接口setWindowLayoutFullScreen设置沉浸式。
说明
默认摄像头挖孔区域不为非安全区域,页面不避让挖孔。
从API Version 12开始,可在module.json5中添加配置项, 摄像头挖孔区域视为非安全区,实现页面默认避让挖孔:
"metadata": [
{
"name": "avoid_cutout",
"value": "true",
}
],
二、expandSafeArea属性
控制组件扩展其安全区域。
expandSafeArea(types?: Array<SafeAreaType>, edges?: Array<SafeAreaEdge>)
三、软键盘避让模式
当用户在输入时,为了确保输入框不会被键盘遮挡,系统提供了避让模式来解决这一问题。开发者可以通过setKeyboardAvoidMode控制虚拟键盘抬起时页面的避让模式,避让模式有上抬模式和压缩模式两种,键盘抬起时默认页面避让模式为上抬模式。
1、设置虚拟键盘抬起时页面的避让模式
setKeyboardAvoidMode(value: KeyboardAvoidMode): void
2、获取虚拟键盘抬起时的页面避让模式
getKeyboardAvoidMode(): KeyboardAvoidMode
四、相关示例
1、实现沉浸式效果
通过设置expandSafeArea属性向顶部和底部扩展安全区实现沉浸式效果。
// xxx.ets
@Entry
@Component
struct SafeAreaExample1 {
build() {
Column() {
// ......
}
.height('100%')
.width('100%')
.backgroundImage($r('app.media.bg'))
.backgroundImageSize(ImageSize.Cover)
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}
}
2、滚动列表底部延伸场景
在列表滚动场景中,滚动时内容可与导航条区域重合,滚动到底部时,底部内容需避让导航条。
设置列表组件List组件的expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM]),扩展列表底部到安全区域。此时List组件显示区域扩大,滚动时列表内容可在导航条区域显示。
// FaqList.ets
@Entry
@Component
struct FaqListPage {
listData: string[] = ['问题1', '问题2', '问题3', '问题4', '问题5', '问题6', '问题7', '问题8', '问题9'];
build() {
Column({
space: 10 }) {
Row() {
Text('常见问题')
.fontSize(22)
}
.height(100)
List({
space: 10 }) {
ForEach(this.listData, (item: string) => {
ListItem() {
Column({
space: 10 }) {
Text(item)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
Text(item + '内容')
.fontSize(16)
.fontColor('#999999')
}
.alignItems(HorizontalAlign.Start)
.padding(15)
.width('100%')
.height(150)
.borderRadius(10)
.backgroundColor(Color.White)
}
}, (item: string) => item)
ListItem() {
Text('已加载全部')
.width('100%')
.textAlign(TextAlign.Center)
.opacity(0.6)
.padding(10)
}
}
.padding(10)
.layoutWeight(1)
.scrollBar(BarState.Off)
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
}
.backgroundColor('#f1f3f5')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}
}
将滚动到底部的提示添加在列表项末尾,由于设置expandSafeArea属性不影响子组件的布局,所以滚动到底部时提示文字默认会避让导航条。
常见效果 | 运行效果 |
---|---|
![]() |
![]() |
3、重要信息被软键盘遮挡
例如下面这个电子邮件的示例,内容由三部分组成:标题栏、内容区域和底部操作栏。当点击输入内容的输入框,软键盘会挡住底部的操作栏,影响用户体验,如下图所示:
开发者可以通过设置软键盘的避让模式为KeyboardAvoidMode.RESIZE(压缩模式),来解决底部操作栏被遮挡的问题,设置该属性后,软键盘的避让会通过压缩内容区域的高度来实现。示例代码如下:
import {
KeyboardAvoidMode } from '@kit.ArkUI';
@Entry
@Component
struct MailPage {
aboutToAppear(): void {
this.getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.RESIZE);
}
aboutToDisappear(): void {
this.getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.OFFSET);
}
build() {
Column() {
// 标题栏
this.NavigationTitle()
// 内容区域
this.EmailContent()
// 操作栏
this.BottomToolbar()
}
.height('100%')
.width('100%')
.backgroundColor('#f1f3f5')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}
@Builder
NavigationTitle() {
Row() {
Image($r('app.media.arrow_left'))
.width(24)
.height(24)
.margin({
right: 16 })
Text('新建电子邮件')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Blank()
Image($r('app.media.paperplane'))
.width(24)
.height(24)
}
.width('100%')
.height(56)
.padding({
left: 24,
right: 24
})
}
@Builder
EmailContent() {
Column() {
this.RowInfo('发件人')
this.RowInfo('收件人')
this.RowInfo('主题')
Row() {
TextArea({
placeholder: '请输入邮件正文' })
.height('100%')
.backgroundColor('#f1f3f5')
}
.layoutWeight(1)
.alignItems(VerticalAlign.Top)
.width('100%')
.margin({
top: 12 })
}.width('100%')
.layoutWeight(1)
.padding({
left: 24, right: 24 })
.margin({
top: 8 })
}
@Builder
RowInfo(param: string) {
Row() {
Text(`${
param}`)
.fontColor('#6f7780')
.fontSize(16)
TextInput({
placeholder: `请输入${
param}` })
.width('100%')
.backgroundColor('#f1f3f5')
}
.width('100%')
.height(48)
.border({
width: {
top: 1 },
color: '#e8ebed'
})
}
@Builder
BottomToolbar() {
Row({
space: 24 }) {
Image($r('app.media.folder'))
.ImageSize()
Image($r('app.media.picture'))
.ImageSize()
Image($r('app.media.arrow_up_circle'))
.ImageSize()
Image($r('app.media.share2'))
.ImageSize()
}
.width('100%')
.height(56)
.padding({
left: 24, right: 24 })
.border({
width: {
top: 1 },
color: '#E8EBED'
})
}
@Styles
ImageSize() {
.height(24)
.width(24)
}
}
4、软键盘弹出导致布局错位
例如下面这样的一个聊天界面,顶部是一个自定义的标题,下方为可滚动聊天消息区域,底部是消息输入框。但是由于软键盘避让默认是上抬模式,会把整个页面向上抬起,所以标题也会被顶上去,如下图所示。现在需求希望顶部标题固定,点击底部输入框软键盘弹起的时候,标题不上抬,只有内容区域上抬。
需要给对应的组件设置 .expandSafeArea([SafeAreaType.KEYBOARD])}属性,使标题组件不避让键盘,示例代码如下:
- 先设置窗口为全屏模式。
// EntryAbility.ets
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
// 获取该WindowStage实例下的主窗口。
const mainWindow = windowStage.getMainWindowSync();
// 设置主窗口的布局是否为沉浸式布局。
mainWindow.setWindowLayoutFullScreen(true).then(() => {
hilog.info(0x0000, 'testTag', 'Succeeded in setting the window layout to full-screen mode');
}).catch((err: BusinessError) => {
hilog.info(0x0000, 'testTag', 'Failed to set the window layout to full-screen mode. Cause: %{public}s', JSON.stringify(err) ?? '');
})
// ...
}
- 再设置标题组件不避让键盘
// ContactPage.ets
@Component
export struct ContactPage {
build() {
Column() {
Row() {
// 顶部自定义标题栏
// ...
}
.height('12%')
.expandSafeArea([SafeAreaType.KEYBOARD]) // 标题组件不避让键盘
.zIndex(1)
List() {
// 聊天消息区域
// ...
}
.height('76%')
Column(){
// 底部消息输入框
// ...
}
.height('12%')
}
.width('100%')
.height('100%')
}
}
5、自定义弹窗被键盘顶起
在软键盘系统避让机制中介绍过,弹窗为避让软键盘会进行避让,整体向上抬,这样可能会影响用户体验。比如下面这个评论里列表的弹窗,使用@CustomDialog实现的。当用户点击弹窗底部的输入框的时候,弹窗会整体上抬,输入框上抬的距离也过多。
为了解决以上问题,可以使用Navigation.Dialog,通过设置NavDestination的mode为NavDestinationMode.DIALOG弹窗类型,此时整个NavDestination默认透明显示,示例代码如下:
// NavDestinationModeDemo.ets
import {
Chat, chatList } from '../model/CommentData';
@Entry
@Component
struct NavDestinationModeDemo {
@Provide('NavPathStack') pageStack: NavPathStack = new NavPathStack()
@Builder
PagesMap(name: string) {
if (name === 'DialogPage') {
DialogPage()
}
}
build() {
Navigation(this.pageStack) {
Column() {
Button('点击评论')
.onClick(() => {
this.pageStack.pushPathByName('DialogPage', '');
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
.mode(NavigationMode.Stack)
.navDestination(this.PagesMap)
}
}
@Component
export struct DialogPage {
@Consume('NavPathStack') pageStack: NavPathStack;
build() {
NavDestination() {
Stack({
alignContent: Alignment.Bottom }) {
Column() {
Row() {
Text('评论')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Blank()
Button() {
Image($r('app.media.cancel'))
.width(18)
}
.padding(10)
.backgroundColor('rgba(0,0,0,0.05)')
.onClick(() => {
this.pageStack.pop();
})
}
.padding(15)
.width('100%')
List({
space:20}) {
ForEach(chatList, (item: Chat) => {
ListItem() {
Row({
space:10}) {
Image(item.profile)
.width(40)
.height(40)
.borderRadius(40)
Column({
space: 10 }) {
Text(item.nickname)
.fontSize(16)
.fontColor('#999999')
Text(item.content)
.fontSize(16)
.fontColor('#333333')
}
.width('100%')
.justifyContent(FlexAlign.Start)
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Top)
}
})
}
.scrollBar(BarState.Off)
.width('100%')
.layoutWeight(1)
TextInput({
placeholder: '写评论' })
.height(40)
.width('100%')
}
.borderRadius({
topLeft: 32,
topRight: 32
})
.backgroundColor(Color.White)
.height('75%')
.width('100%')
.padding(10)
}
.height('100%')
.width('100%')
}
.backgroundColor('rgba(0,0,0,0.2)')
.hideTitleBar(true)
.mode(NavDestinationMode.DIALOG)
}
}
此外还需要设置软键盘避让模式为压缩模式,示例代码如下:
// EntryAbility.ets
import {
KeyboardAvoidMode, window } from '@kit.ArkUI';
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/CustomDialogDemoPage', (err) => {
// 设置虚拟键盘抬起时压缩页面大小为减去键盘的高度
windowStage.getMainWindowSync().getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.RESIZE);
});
}
点击下方按钮添加微信,领取资料和学习方案。一键三连+关注,你的支持是我创作的动力。在这里,我乐于倾囊相授。