浅谈适配沉浸式页面
·
🎯 浅谈适配沉浸式页面
⭐⭐⭐⭐
📢 前言
沉浸式模式通常指让应用的界面更加专注于内容,不希望用户被无关元素干扰。在移动端应用中,全屏窗口元素包括状态栏、应用界面和导航栏
📌 见解
1️⃣ 使页面和避让区域的色调统一,为用户提供更好的视觉体验
2️⃣ 最大程度利用屏幕可视区域,使页面获得更大的布局空间
3️⃣ 提供完全沉浸的体验,让用户沉浸其中,不被其他事物所干扰
⚠️ 使用场景
-
顶部或底部背景延伸
-
顶部图片延伸
-
拱洞列表底部延伸
-
全屏沉浸式
-
挖空区避让
-
深色背景喜爱状态栏颜色适配
🧩 拆解
🧱 顶部或底部背景延伸
// 方案一:使用expandSafeArea属性扩展背景组件安全区域
Column()
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor('#F1F3F5')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
// 方案二:使用Window.setWindowLayoutFullScreen()方法设置窗口全屏模式
// 1、定义相关关键字和接口
const statusBarType = window.AvoidAreaType.TYPE_SYSTEM
const navBarType = window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR
interface AvoidArea {
topRectHeight: number
bottomRectHeight: number
}
// 2、初始化状态
context = this.getUIContext()?.getHostContext() as common.UIAbilityContext
private windowClass = this.context.windowStage.getMainWindowSync()
@State avoidArea: AvoidArea = { topRectHeight: 0, bottomRectHeight: 0 }
// 3、设置状态栏和导航栏避让区域 && 监听不同设备避让区域的变化
setAvoidArea() {
const statusBarArea = this.windowClass.getWindowAvoidArea(statusBarType)
this.avoidArea.topRectHeight = statusBarArea.topRect.height
const navBarArea = this.windowClass.getWindowAvoidArea(navBarType)
this.avoidArea.bottomRectHeight = navBarArea.bottomRect.height
}
onAvoidAreaChange = (data: window.AvoidAreaOptions) => {
if (data.type === statusBarType) {
this.avoidArea.topRectHeight = data.area.topRect.height;
} else if (data.type === navBarType) {
this.avoidArea.bottomRectHeight = data.area.bottomRect.height;
}
}
// 4、生命周期中调用
aboutToAppear(): void {
this.windowClass.setWindowLayoutFullScreen(true)
this.setAvoidArea()
this.windowClass.on('avoidAreaChange', this.onAvoidAreaChange)
}
aboutToDisappear(): void {
this.windowClass.setWindowLayoutFullScreen(false)
this.windowClass.off('avoidAreaChange', this.onAvoidAreaChange)
}
// 5、视图使用
Column()
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor('#F1F3F5')
.padding({
top: this.avoidArea.topRectHeight,
bottom: this.avoidArea.bottomRectHeight
})
🧱 顶部图片延伸场景
Scroll() {
Column() {
Swiper(this.swiperController) {
ForEach(['bakc'], (image: string) => {
Image($r(`app.media.${image}`))
.width('100%')
.height('100%')
}, (image: Resource) => JSON.stringify(image))
}
.height(200)
Text('汉堡黄🍔')
.fontSize(20)
.fontWeight(FontWeight.Bold)
x .height(1000)
}
}
.height('100%')
.width('100%')
.backgroundColor('#F1F3F5')
// TODO: 使用延伸属性扩展出去
// .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
// 设置如案例一设置顶部安全区边距
.padding({ top: this.avoidArea.topRectHeight + 'px' })
🧱 滚动列表底部延伸场景
Column() {
List({ space: 20 }) {
ListItem() {
Column() {
Text('1')
}
.height(1000)
}
.width('100%')
.backgroundColor(Color.Green)
ListItem() {
Column() {
Text('2')
}
.width('100%')
.backgroundColor(Color.Gray)
.height(1000)
}
}
.layoutWeight(1)
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
}
.width('100%')
.height('100%')
🧱 全屏沉浸式场景
aboutToAppear(): void {
// 在自己业务设置以下代码
this.windowClass.setWindowSystemBarEnable([])
}
aboutToDisappear(): void {
this.windowClass.setWindowSystemBarEnable(['status', 'navigation'])
}
🧱 挖空区避让
/**
* 通过比较挖孔区域距屏幕左侧边缘和右侧边缘的距离,判断挖孔区域在屏幕左侧、右侧还是中间。
* 挖孔在中间无需计算两侧按钮左右边距,默认为0。
* 挖孔在左边,则左侧按钮左边距 = 挖孔距屏幕左侧边缘的距离 + 挖孔区域的宽度。
* 挖孔在右侧,则右侧按钮右边距 = 挖孔距屏幕右侧边缘的距离 + 挖孔区域的宽度。
*/
// 1、为了方便测试src/main/module.json5
"abilities": [
{
"orientation": "auto_rotation", // 支持自动旋转
}
]
// 2、视图使用
aboutToAppear(): void {
// 在自己业务设置以下代码
this.windowClass.setWindowSystemBarEnable([])
}
aboutToDisappear(): void {
this.windowClass.setWindowSystemBarEnable(['status', 'navigation'])
}
完整代码
// 1、为了方便测试src/main/module.json5
//"abilities": [
// {
// "orientation": "auto_rotation", // 支持自动旋转
// }
//]
import { common } from '@kit.AbilityKit';
import { display, window } from '@kit.ArkUI';
const statusBarType = window.AvoidAreaType.TYPE_SYSTEM
const navBarType = window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR
interface AvoidArea {
topRectHeight: number
bottomRectHeight: number
}
interface StatusBarMargin {
left: number
right: number
}
@Entry
@Component
struct Index {
context = this.getUIContext()?.getHostContext() as common.UIAbilityContext
private windowClass = this.context.windowStage.getMainWindowSync()
private displayClass = display.getDefaultDisplaySync()
@State avoidArea: AvoidArea = { topRectHeight: 0, bottomRectHeight: 0 }
private swiperController: SwiperController = new SwiperController()
@State statusBarMargin: StatusBarMargin = { left: 0, right: 0 }
async setStatusBarMargin(windowWidth: number) {
const cutoutInfo = await this.getCutoutInfo()
this.statusBarMargin = this.getStatusBarMargin(cutoutInfo, windowWidth)
}
async getCutoutInfo() {
const res = await this.displayClass.getCutoutInfo()
return res.boundingRects
}
getStatusBarMargin(cutoutInfo: display.Rect[], windowWidth: number): StatusBarMargin {
if (!cutoutInfo || cutoutInfo.length === 0) {
return { left: 0, right: 0 }
}
const cutoutRect = cutoutInfo[0];
const cutoutLeftGap = cutoutRect.left
const cutoutWidth = cutoutRect.width
const cutoutRightGap = windowWidth - cutoutLeftGap - cutoutWidth;
if (Math.abs(cutoutLeftGap - cutoutRightGap) <= 10) {
return { left: 0, right: 0 }
}
if (cutoutLeftGap < cutoutRightGap) {
return { left: cutoutLeftGap + cutoutWidth, right: 0 }
}
return { left: 0, right: cutoutRightGap + cutoutWidth }
}
aboutToAppear(): void {
// TODO: 测试组件延伸属性需要将下一段代码注释掉
this.windowClass.setWindowLayoutFullScreen(true)
this.setAvoidArea()
this.windowClass.on('avoidAreaChange', this.onAvoidAreaChange)
this.windowClass.setWindowSystemBarEnable([])
this.windowClass.on('windowSizeChange',(data) => {
this.setStatusBarMargin(display.getDefaultDisplaySync().width)
})
}
aboutToDisappear(): void {
this.windowClass.setWindowLayoutFullScreen(false)
this.windowClass.off('avoidAreaChange', this.onAvoidAreaChange)
this.windowClass.setWindowSystemBarEnable(['status', 'navigation'])
}
setAvoidArea() {
const statusBarArea = this.windowClass.getWindowAvoidArea(statusBarType)
this.avoidArea.topRectHeight = statusBarArea.topRect.height
const navBarArea = this.windowClass.getWindowAvoidArea(navBarType)
this.avoidArea.bottomRectHeight = navBarArea.bottomRect.height
}
onAvoidAreaChange = (data: window.AvoidAreaOptions) => {
if (data.type === statusBarType) {
this.avoidArea.topRectHeight = data.area.topRect.height
} else if (data.type === navBarType) {
this.avoidArea.bottomRectHeight = data.area.bottomRect.height
}
}
build() {
// TODO: 🧱 **滚动列表底部延伸场景** demo
// Column() {
// List({ space: 20 }) {
// ListItem() {
// Column() {
// Text('1')
// }
// .height(1000)
// }
// .width('100%')
// .backgroundColor(Color.Green)
//
// ListItem() {
// Column() {
// Text('2')
// }
// .width('100%')
// .backgroundColor(Color.Gray)
// .height(1000)
// }
// }
// .layoutWeight(1)
// .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
// }
// .width('100%')
// .height('100%')
// TODO: 🧱 **顶部图片延伸场景 || 顶部或底部背景延伸** demo
// Scroll() {
// Column() {
// Swiper(this.swiperController) {
// ForEach(['bakc'], (image: string) => {
// Image($r(`app.media.${image}`))
// .width('100%')
// .height('100%')
// }, (image: Resource) => JSON.stringify(image))
// }
// .height(200)
//
// Text('汉堡黄🍔')
// .fontSize(20)
// .fontWeight(FontWeight.Bold)
// .height(1000)
// }
// }
// .height('100%')
// .width('100%')
// .backgroundColor('#F1F3F5')
// // .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
// // .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
// .padding({
// top: this.avoidArea.topRectHeight + 'px',
// bottom: this.avoidArea.bottomRectHeight + 'px'
// })
// TODO: 🧱 **挖空区避让** demo
Row() {
Text('我叫汉堡黄')
}
.width('100%')
.height('100%')
.backgroundColor(Color.Gray)
.margin({ left: this.statusBarMargin.left + 'px', right: this.statusBarMargin.right + 'px' })
}
}
📝 沉浸式设计通过消除视觉边界、强化内容聚焦、动态适配系统特性(如深色模式、多窗口形态),为用户提供更流畅、舒适的体验,同时为开发者提供标准化的技术方案(如全屏接口、避让区监听),降低多设备适配成本
🌸🌼🌺
更多推荐



所有评论(0)