讨论广场 问答详情
【求助】导航栏、状态栏下拉栏的全局控制
针尖拔麦芒 2025-10-16 11:51:14
275 评论 分享

系统和硬件环境:OpenHarmony5.0.0, 硬件RK3566

需求:实现全局中导航栏、状态栏和下拉栏的隐藏和显示

问题描述:在SystemUI中实现了一个控制器,专门用于控制导航栏、下拉栏和状态栏的显示和隐藏,功能看上去好像实现了,但是存在两个问题

1. 这个控制中心应该放在哪里?当要对这些组件进行控制的时候,需要一个载体让用户可以按需设置,是悬浮、桌面应用还是系统设置里面的选项。尝试了改造下拉栏的通知栏,但是如果禁用了下拉栏不能返回控制中心,使用上存在局限性;

2. 打开最近任务栏后,隐藏失效。在控制中心完成设置后,从屏幕底部向上滑动可以返回桌面,此时桌面和打开其他应用时,状态栏和导航栏均可以隐藏。但是当从屏幕右侧底部向上滑动时会打开最近任务栏(应该是launcher的组件),通过手势返回桌面后,隐藏的导航栏和状态栏显示了。

控制器代码如下所示:

export const SYSTEMUI_VISIBILITY_CHANGE = 'SystemUIVisibilityChange';

export interface SystemUIState {
  statusBarVisible: boolean;
  navigationBarVisible: boolean;
  dropdownEnabled: boolean;
  kioskModeEnabled: boolean;
}

class SystemUIController {
  private currentState: SystemUIState = {
    statusBarVisible: true,
    navigationBarVisible: true,
    dropdownEnabled: true,
  };

  init(): void {
    Log.showInfo(TAG, 'SystemUIController initializing...');
    this.updateAppStorage();
    Log.showInfo(TAG, 'SystemUIController initialized successfully');
  }

  private updateAppStorage(): void {
    AppStorage.SetOrCreate('statusBarVisible', this.currentState.statusBarVisible);
    AppStorage.SetOrCreate('navigationBarVisible', this.currentState.navigationBarVisible);
    AppStorage.SetOrCreate('dropdownEnabled', this.currentState.dropdownEnabled);
    AppStorage.SetOrCreate('kioskModeEnabled', this.currentState.kioskModeEnabled);
  }

  private publishStateChange(): void {
    EventManager.publish(obtainLocalEvent(SYSTEMUI_VISIBILITY_CHANGE, {
      state: { ...this.currentState }
    }));
    Log.showDebug(TAG, `State changed: ${JSON.stringify(this.currentState)}`);
  }

  async showStatusBar(): Promise<void> {
    Log.showInfo(TAG, 'Showing StatusBar');
    this.currentState.statusBarVisible = true;
    this.currentState.dropdownEnabled = true;
    await WindowManager.showWindow(WindowType.STATUS_BAR);
    this.updateAppStorage();
    this.publishStateChange();
  }

  async hideStatusBar(): Promise<void> {
    Log.showInfo(TAG, 'Hiding StatusBar');
    this.currentState.statusBarVisible = false;
    this.currentState.dropdownEnabled = false;
    await WindowManager.hideWindow(WindowType.STATUS_BAR);
    this.updateAppStorage();
    this.publishStateChange();
  }

  async showNavigationBar(): Promise<void> {
    Log.showInfo(TAG, 'Showing NavigationBar');
    this.currentState.navigationBarVisible = true;
    await WindowManager.showWindow(WindowType.NAVIGATION_BAR);
    this.updateAppStorage();
    this.publishStateChange();
  }

  async hideNavigationBar(): Promise<void> {
    Log.showInfo(TAG, 'Hiding NavigationBar');
    this.currentState.navigationBarVisible = false;
    await WindowManager.hideWindow(WindowType.NAVIGATION_BAR);
    this.updateAppStorage();
    this.publishStateChange();
  }

  enableDropdown(): void {
    if (this.currentState.statusBarVisible) {
      Log.showInfo(TAG, 'Enabling Dropdown');
      this.currentState.dropdownEnabled = true;
      this.updateAppStorage();
      this.publishStateChange();
    } else {
      Log.showWarn(TAG, 'Cannot enable dropdown: StatusBar is hidden');
    }
  }

  disableDropdown(): void {
    Log.showInfo(TAG, 'Disabling Dropdown');
    this.currentState.dropdownEnabled = false;
    this.updateAppStorage();
    this.publishStateChange();
  }

  getState(): SystemUIState {
    return { ...this.currentState };
  }

  async toggleStatusBar(): Promise<void> {
    if (this.currentState.statusBarVisible) {
      await this.hideStatusBar();
    } else {
      await this.showStatusBar();
    }
  }

  async toggleNavigationBar(): Promise<void> {
    if (this.currentState.navigationBarVisible) {
      await this.hideNavigationBar();
    } else {
      await this.showNavigationBar();
    }
  }
}

let sSystemUIController = createOrGet(SystemUIController, TAG);

export default sSystemUIController as SystemUIController;

在隐藏和显示时都会调用WindowManager的show和hide函数,然后再调用window的show和hide函数。

查看这些组件代码,ServiceExtAbility中会实现窗口这些组件对应窗口的整个过程,创建之后显示,整个过程看上去都没有问题。但是我觉得我的控制逻辑实现上应该是存在问题的,查看了源码的事件管理,viewmodel等内容,查看了一下安卓上的实现方式:Android11动态设置显示隐藏导航栏 - M-kobe - 博客园 (cnblogs.com),虽然没看的太懂,但是感觉实现上应该不是很难,由于缺乏系统整体的把握,经常会陷入具体的实现细节上,还是没有找到一个合适的实现方式。

请教各位大佬给出一个实现思路,不胜感激!

275 评论 分享
写回答
全部评论(11)
1 楼

1.或许你可以在设置中加一个 显示或隐藏的属性,然后对应的 设置systemui中导航栏NavigationBar和状态栏StatusBar的 高度 为0 或 非0 就能相对快速的解决你的问题。

2.当前 动态显隐 是更好的选择。

2025-10-16 12:19:45
2025-10-29 16:26:12
引用:“1.systemui 中的 NavigationBarViewModel 里会监听,同时getValueSync 来动态显/隐导航栏,显/隐 状态栏 也可以在这里加,这样导航栏状态栏的显隐就都走导航栏的显隐开关属性了。 2.或者在launcher 新增隐藏状态栏的事件,同样在systemui中增加状态栏显/隐的监听。这样可以区分导航栏或状态栏的显/隐。”
多谢老哥,我现在的实现方式就是这样子,粗粒度的实现就是将控制导航栏和状态栏显示放在同一个开关上面,只需要在监听到值发生变化后(改变开关状态),就会显示/隐藏,另外也添加了新的值,用来专门控制状态栏。
2025-10-29 16:26:12
2025-10-27 10:15:31
引用:“谢谢老哥!请问方便添加一个您的联系方式嘛,感谢您多次的回答,给了我很多帮助,请您喝杯咖啡啥的”
1.systemui 中的 NavigationBarViewModel 里会监听,同时getValueSync 来动态显/隐导航栏,显/隐 状态栏 也可以在这里加,这样导航栏状态栏的显隐就都走导航栏的显隐开关属性了。 2.或者在launcher 新增隐藏状态栏的事件,同样在systemui中增加状态栏显/隐的监听。这样可以区分导航栏或状态栏的显/隐。
2025-10-27 10:15:31
2025-10-27 08:33:50
引用:“晚点我写一下 给你”
谢谢老哥!请问方便添加一个您的联系方式嘛,感谢您多次的回答,给了我很多帮助,请您喝杯咖啡啥的
2025-10-27 08:33:50
2025-10-24 09:30:17
引用:“老哥我还得向你请教一下,我现在还是在实现动态显示/隐藏,我现在坚定的使用launcher中的手势导航来实现。我追踪了手势导航开关的实现,其核心是使用dataShare机制,我发现是当点击桌面设置页面中按钮时,会通过sendlocalEvent函数然后调用setValueSync将数据写到系统中,手势导航管理器中注册的数据变化监听器进行监听,当监听到数据变化最终会调用localEventManage”
晚点我写一下 给你
2025-10-24 09:30:17
2025-10-23 16:48:56
老哥我还得向你请教一下,我现在还是在实现动态显示/隐藏,我现在坚定的使用launcher中的手势导航来实现。我追踪了手势导航开关的实现,其核心是使用dataShare机制,我发现是当点击桌面设置页面中按钮时,会通过sendlocalEvent函数然后调用setValueSync将数据写到系统中,手势导航管理器中注册的数据变化监听器进行监听,当监听到数据变化最终会调用localEventManager中的sendlocalEvent,监听器会响应onReceiveEvent,然后EntryView中最终会更新屏幕信息。我按照同样的方式来控制状态栏和下拉栏,现在还是不能实现这个功能,我就很奇怪这个功能到底应该如何实现呢。还是不是很理解这里面的机制,因为这里面的导航栏隐藏和显示似乎不是通过窗口来实现的,还能如何对其进行控制呢。感谢感谢
2025-10-23 16:48:56
2025-10-17 10:14:38
引用:“我先尝试了在我写的systemuicontroller中调用hide函数后,添加一个resize调用,将窗口高度设置为0,还是有点奇怪的问题,resize后,导航栏位置会略微发生变化,如果单独添加resize调用将窗口高度置为0,看上去没有任何影响。但是观察最近任务栏和设置后的交互有点疑惑,打开了最近任务栏,这个界面是全屏,而且不会显示显示导航栏和状态栏,是一个单独的全屏界面,当返回到桌面后,导航”
高度设置为0 和 手势导航是两种方案。 为0监听不到滑动距离,所以不会触发。 而手势导航是在launcher上 边缘滑动的距离来计算的。 如果当前效果满足你的使用需求,先这么调试就好了。
2025-10-17 10:14:38
2025-10-17 10:01:44
引用:“最近任务栏 是在launcher里的Recent窗口,他是系统应用 通用有有权限对导航栏或状态栏做显示或隐藏。 这里还是回到 说的显隐的地方,涉及可以修改 导航栏或状态栏 的显示或隐藏的地方 都需要根据你配置的SystemUIState来。 当然还包括launcher的 手势导航。 你可以参考手势导航【显隐导航栏的做法】,再把状态栏的显/隐加上就好了。”
我先尝试了在我写的systemuicontroller中调用hide函数后,添加一个resize调用,将窗口高度设置为0,还是有点奇怪的问题,resize后,导航栏位置会略微发生变化,如果单独添加resize调用将窗口高度置为0,看上去没有任何影响。但是观察最近任务栏和设置后的交互有点疑惑,打开了最近任务栏,这个界面是全屏,而且不会显示显示导航栏和状态栏,是一个单独的全屏界面,当返回到桌面后,导航栏和状态栏会正常显示,感觉就想是重新创建了一下界面,但是,下拉栏又确实不能下拉。有点没太明白这个交互过程,我在launcher的setting feature中,看到里面对导航栏的控制似乎不是通过窗口实现,而是通过dataShare完成。我理解的是这个setting界面会监听是否点击到手势导航开关,监听到之后,则发送消息/事件/数据到系统,由系统根据发送的内容完成对应的处理。但是我看setting里面的内容似乎和手势导航里面代码没有调用关系,为什么设置完成后,可以完全屏蔽从底部向上滑动的手势。感觉好神奇
2025-10-17 10:01:44
2025-10-16 14:20:06
引用:“最近任务栏 是在launcher里的Recent窗口,他是系统应用 通用有有权限对导航栏或状态栏做显示或隐藏。 这里还是回到 说的显隐的地方,涉及可以修改 导航栏或状态栏 的显示或隐藏的地方 都需要根据你配置的SystemUIState来。 当然还包括launcher的 手势导航。 你可以参考手势导航【显隐导航栏的做法】,再把状态栏的显/隐加上就好了。”
尝试过这种方式,实现上还是不太对,我再看看launcher中的这一部分是如何实现的
2025-10-16 14:20:06
2025-10-16 14:03:40
引用:“可以更加具体说一下第二点嘛,我也在想要是快速解决当前困境就是再调用隐藏接口的时候将窗口高度设置为0,这样子从界面上来看,应该是可以达到隐藏的效果。但是我觉得应该是有一种控制方法。从现在的情况看,基本上实现了全局生效的结果,只是会被最近任务栏给破坏。而让用户控制的界面如果设置在了系统设置选项里面,那我们如果将导航栏状态栏和下拉栏全部隐藏后,进入某个应用是无法退出,要想方便的设置只能重启设备才行。我不”
最近任务栏 是在launcher里的Recent窗口,他是系统应用 通用有有权限对导航栏或状态栏做显示或隐藏。 这里还是回到 说的显隐的地方,涉及可以修改 导航栏或状态栏 的显示或隐藏的地方 都需要根据你配置的SystemUIState来。 当然还包括launcher的 手势导航。 你可以参考手势导航【显隐导航栏的做法】,再把状态栏的显/隐加上就好了。
2025-10-16 14:03:40
2025-10-16 13:07:35
可以更加具体说一下第二点嘛,我也在想要是快速解决当前困境就是再调用隐藏接口的时候将窗口高度设置为0,这样子从界面上来看,应该是可以达到隐藏的效果。但是我觉得应该是有一种控制方法。从现在的情况看,基本上实现了全局生效的结果,只是会被最近任务栏给破坏。而让用户控制的界面如果设置在了系统设置选项里面,那我们如果将导航栏状态栏和下拉栏全部隐藏后,进入某个应用是无法退出,要想方便的设置只能重启设备才行。我不太确定是我需求没弄明白还是我多想了
2025-10-16 13:07:35
查看全部11条回答/评论