HarmonyOS 角落里的知识 —— AvoidArea

922 阅读3分钟

一、前言

在探索 HarmonyOS 的过程中,我们发现了许多有趣且实用的功能和特性。有些总是在不经意间或者触类旁通的找到。其中,AvoidArea 是窗口内容需要规避区域,也是我们在处理沉浸式、全屏时遇到的。

该系列将着重分享、介绍HarmonyOS API11+的新版本特性或者奇奇怪怪的解决方案、BUG。(弃用API非必要不提及)

如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏

二、沉浸式

和窗口相关的API几乎都在@ohos.window模块下,直接使用window.setWindowLayoutFullScreen(true)即可获得一个沉浸式效果。此时布局不避让状态栏与导航栏,组件与其重叠。

一般而言你可以在任何地方使用await window.getLastWindow(getContext())来获得当前的window对象,subwindow不在此列。当然它也有对应的callback写法(但是,是await不好用吗?)

 import { BusinessError } from '@ohos.base';
 ​
 let isLayoutFullScreen = true;
 try {
   let promise = windowClass.setWindowLayoutFullScreen(isLayoutFullScreen);
   promise.then(() => {
     console.info('Succeeded in setting the window layout to full-screen mode.');
   }).catch((err: BusinessError) => {
     console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(err));
   });
 } catch (exception) {
   console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(exception));
 }

三、全屏

全屏一般是指隐藏NavigationBar和StatusBar,使用setWindowSystemBarEnable,传入一个空的数组。就可以获得一个光溜溜的界面了。(此时自带沉浸式,毕竟沉浸的地方都没了)

 let win = await window.getLastWindow(getContext())
 win.setWindowSystemBarEnable([])

目前该方法接收一个数组对象,setWindowSystemBarEnable(names: Array<'status' | 'navigation'>),不显示哪个,就不传入哪个便好。

还有一个API,用于单独控制,而且多了一个导航条的控制,但是其表现在API12上,完全不符合预期。所以不做赘述

 win.setSpecificSystemBarEnabled("status",false)
 win.setSpecificSystemBarEnabled("navigation",false)
 win.setSpecificSystemBarEnabled("navigationIndicator",true)

四、AvoidArea

这是正主了,往往沉浸式的时候,需要去处理组件被系统栏盖住的情况。此时你可以使用getWindowAvoidArea(type: AvoidAreaType): AvoidArea

 let type = window.AvoidAreaType.TYPE_SYSTEM;
 try {
   let avoidArea = windowClass.getWindowAvoidArea(type);
 } catch (exception) {
   console.error('Failed to obtain the area. Cause:' + JSON.stringify(exception));
 }

image-20240521105139995

可以看到,接收5种类型。其中TYPE_NAVIGATION_INDICATOR是为了解决,沉浸式的时候,获取TYPE_SYSTEM底部规避区域高度为0的BUG,而诞生的新Type。

AvoidArea是一个拥有4个Rect的容器。

image-20240521105514341

值得注意的是:

  • 底部手势区域中非导航条区域支持点击、长按事件透传,不支持拖入。
  • 左右侧边手势区域支持点击、长按以及上下滑动事件透传,不支持拖入。
  • 导航条区域支持长按、点击、拖入事件响应,不支持事件向下透传。

有时候,规避区域是会变化的,监听一下就好了。反过来off解除。

 try {
   windowClass.on('avoidAreaChange', (data) => {
     console.info('Succeeded in enabling the listener for system avoid area changes. type:' +
     JSON.stringify(data.type) + ', area: ' + JSON.stringify(data.area));
   });
 } catch (exception) {
   console.error('Failed to enable the listener for system avoid area changes. Cause: ' + JSON.stringify(exception));
 }

五、expandSafeArea

这个API是在做自定义键盘面板的时候发现的。实际上它完全可以用来做沉浸式,而且更加的方便,只在加了的地方生效,而不像window直接生效在整个页面。

使用起来也很简单,直接在根组件(任意组件都行)

 build() {
     Column() {
         Column() {
            
         }
         .width('100%')
         .height('100%')
     }
     .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM,SafeAreaEdge.TOP])
     .backgroundColor(Color.Orange)
     .size({ width: '100%', height: '100%' })
 }

此时,和window.setWindowLayoutFullScreen(true)的效果是一模一样的。

哦,这个要API 11。

六、预告

下篇分享点击(可能)

七、结尾

没了。

如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏