1、概 述
本文是接着上篇文章 鸿蒙UI开发——基于全屏方案实现沉浸式界面 的继续讨论。除了全屏方案实现沉浸式界面外,我们还可以使用组件安全区的方案。
当我们没有使用setWindowLayoutFullScreen()接口设置窗口为全屏布局时,默认使用的策略就是组件安全区布局方式。即:
【应用在默认情况下窗口背景绘制范围是全屏,但UI元素被限制在安全区内(自动排除状态栏和导航条)进行布局,来避免界面元素被状态栏和导航条遮盖】
示意图如下:
我们将按业务场景去分析基于组件安全区的沉浸式界面的开发。
2、状态栏和导航条颜色相同场景
针对有些APP,状态栏和导航条颜色可能相同,例如如下效果:
针对这种场景,我们可以通过设置窗口的背景色来实现沉浸式效果,设置方式为:setWindowBackgroundColor() 【将窗口的颜色和应用的颜色设置为一致】
Ability中设置window的背景色代码如下(请注意14行代码):
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
// ...
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', (err, data) => {
if (err.code) {
return;
}
// 设置全窗颜色和应用元素颜色一致
windowStage.getMainWindowSync().setWindowBackgroundColor('#008000');
});
}
}
组件中的背景色设置代码如下(请注意35行代码):
// xxx.ets
@Entry
@Component
struct Example {
build() {
Column() {
Row() {
Text('ROW1').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
Row() {
Text('ROW2').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
Row() {
Text('ROW3').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
Row() {
Text('ROW4').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
Row() {
Text('ROW5').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
Row() {
Text('ROW6').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
}
.width('100%')
.height('100%')
.alignItems(HorizontalAlign.Center)
.justifyContent(FlexAlign.SpaceBetween)
.backgroundColor('#008000')
}
}
实现效果如下:
3、状态栏和导航条颜色不同场景
状态栏和导航条颜色不同时,可以使用expandSafeArea属性扩展安全区域属性进行调整。示例代码如下:
// xxx.ets
@Entry
@Component
struct Example {
build() {
Column() {
Row() {
Text('Top Row').fontSize(40).textAlign(TextAlign.Center).width('100%')
}
.backgroundColor('#F08080')
// 设置顶部绘制延伸到状态栏
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
Row() {
Text('ROW2').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
Row() {
Text('ROW3').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
Row() {
Text('ROW4').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
Row() {
Text('ROW5').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
Row() {
Text('Bottom Row').fontSize(40).textAlign(TextAlign.Center).width('100%')
}
.backgroundColor(Color.Orange)
// 设置底部绘制延伸到导航条
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
}
.width('100%').height('100%').alignItems(HorizontalAlign.Center)
.backgroundColor('#008000')
.justifyContent(FlexAlign.SpaceBetween)
}
}
效果如下:
【扩展安全区域属性原理】
|
4、背景图和视频场景
设置背景图、视频控件大小为安全区域大小并配置expandSafeArea属性。
// xxx.ets
@Entry
@Component
struct SafeAreaExample1 {
build() {
Stack() {
Image($r('app.media.bg'))
.height('100%').width('100%')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) // 图片组件的绘制区域扩展至状态栏和导航条。
}.height('100%').width('100%')
}
}
效果图如下:
5、滚动类场景
场景需求:需要List滚动类组件滚动过程中元素可以和导航条重合,滚动至底部时,元素在导航条上面需要避让。示意图如下:
由于expandSafeArea不改变子节点布局,因此,List等滚动类组件可以调用expandSafeArea,延伸List组件视图窗口大小而不改变ListItem内在布局。实现ListItem在滑动过程中显示在导航条下,但滚动至最后一个时显示在导航条上。
开发示例如下:
step 1 : 配置窗口整体底色
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
// ...
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', (err, data) => {
if (err.code) {
return;
}
windowStage.getMainWindowSync().setWindowBackgroundColor('#DCDCDC'); // 配置窗口整体底色
});
}
}
step 2 : 界面代码展示
// xxx.ets
@Entry
@Component
struct ListExample {
private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
build() {
Column() {
List({ space: 20, initialIndex: 0 }) {
ForEach(this.arr, (item: number) => {
ListItem() {
Text('' + item)
.width('100%')
.height(100)
.fontSize(16)
.textAlign(TextAlign.Center)
.borderRadius(10)
.backgroundColor(0xFFFFFF)
}
}, (item: string) => item)
}
.listDirection(Axis.Vertical) // 排列方向
.scrollBar(BarState.Off)
.friction(0.6)
.divider({ strokeWidth: 2, color: 0xFFFFFF, startMargin: 20, endMargin: 20 }) // 每行之间的分界线
.edgeEffect(EdgeEffect.Spring) // 边缘效果设置为Spring
.width('90%')
// List组件的视窗范围扩展至导航条。
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}
.width('100%')
.height('100%')
.padding({ top: 15 })
}
}
6、底部页签场景
场景需求:页签背景色能够延伸到导航条区域,但页签内部可操作元素需要在导航条之上。
针对底部的页签部分,Navigation组件和Tabs组件默认实现了页签的延伸处理,我们使用时只需要保证Navigation和Tabs组件的底部边界和底部导航条重合即可。
若我们显式调用expandSafeArea接口,则安全区效果由expandSafeArea参数指定。
如果我们没有使用Navigation和Tab组件而是采用自定义方式实现页签的场景,可以针对底部元素设置expandSafeArea属性实现底部元素的背景扩展。
顶部和底部UI元素未设置和设置expandSafeArea属性效果对比如下:
沉浸式实现代码如下:
// xxx.ets
@Entry
@Component
struct VideoCreateComponent {
build() {
Column() {
Row() {
Text('Top Row').fontSize(40).textAlign(TextAlign.Center).width('100%')
}
.backgroundColor('#F08080')
// 设置顶部绘制延伸到状态栏
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
Row() {
Text('ROW2').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
Row() {
Text('ROW3').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
Row() {
Text('ROW4').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
Row() {
Text('ROW5').fontSize(40)
}.backgroundColor(Color.Orange).padding(20)
Row() {
Text('Bottom Row').fontSize(40).textAlign(TextAlign.Center).width('100%')
}
.backgroundColor(Color.Orange)
// 设置底部绘制延伸到导航条
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
}
.width('100%').height('100%').alignItems(HorizontalAlign.Center)
.justifyContent(FlexAlign.SpaceBetween)
.backgroundColor(Color.Green)
}
}
7、图文场景
当状态栏元素和底部导航条元素不同时,无法单纯通过窗口背景色或者背景图组件延伸实现,此时需要对顶部元素和底部元素分别配置expandSafeArea属性,顶部元素配置expandSafeArea([SafeAreaType.SYSTEM],[SafeAreaEdge.TOP]),底部元素配置expandSafeArea([SafeAreaType.SYSTEM],[SafeAreaEdge.BOTTOM])。
代码如下:
@Entry
@Component
struct Index {
build() {
Swiper() {
Column() {
Image($r('app.media.start'))
.height('50%').width('100%')
// 设置图片延伸到状态栏
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
Column() {
Text('HarmonyOS 第一课')
.fontSize(32)
.margin(30)
Text('通过循序渐进的学习路径,无经验和有经验的开发者都可以掌握ArkTS语言声明式开发范式,体验更简洁、更友好的HarmonyOS应用开发旅程。')
.fontSize(20).margin(20)
}.height('50%').width('100%')
.backgroundColor(Color.White)
// 设置文本内容区背景延伸到导航栏
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
}
}
.width('100%')
.height('100%')
// 关闭Swiper组件默认的裁切效果以便子节点可以绘制在Swiper外。
.clip(false)
}
}
实现效果如下: