跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Android TaskbarDelegate 屏蔽上滑返回桌面手势

Android 14 平台下,通过修改 SystemUI 中 TaskbarDelegate 的逻辑,利用 TaskStackChangeListener 监听任务栈变化,动态设置 SYSUI_STATE_OVERVIEW_DISABLED 和 SYSUI_STATE_HOME_DISABLED 标志位,实现特定应用界面屏蔽上滑返回桌面手势的功能,无需拦截输入事件流。

芝士奶盖发布于 2026/4/6更新于 2026/9/872 浏览

需求描述

基于 Android 14 平台,在应用界面中默认使用上滑手势会返回桌面,某些应用要求不能返回到桌面,需要屏蔽上滑手势。

需求分析

我们知道手势导航的底部导航横线的逻辑是在 SystemUI 的 NavigationBarView -> NavigationBar -> NavigationBarController -> TaskbarDelegate 等相关的类控制,在 手势导航 中上滑手势有 2 种功能:

  • 从屏幕底部向上滑动,即可进入主屏幕
  • 从底部向上滑动、按住再松开,可切换应用

如果想要屏蔽,可以直接返回消费掉 InputEvent 事件,就不会走后续的流程了。我尝试在 NavigationBarView 的 onInterceptTouchEvent 返回 true,但是只屏蔽了返回 Home 的功能,最近任务还是会启动。在 TaskbarDelegate 中有设置相关的 sys ui flag 的相关逻辑,于是我们可以参考类似方案,实现类似的屏蔽效果。

  1. TaskbarDelegate 初始化时添加需要屏蔽上滑功能的 Activity 合集
  2. 创建一个监听器 TaskStackChangeListener,用于监听前台应用是否需要屏蔽,并注册监听
  3. 任务栈变化时候改变 SysuiFlag,如 SYSUI_STATE_OVERVIEW_DISABLED 和 SYSUI_STATE_HOME_DISABLED

这个方案的优点是,我们不需要去追踪复杂的输入事件流,而是直接通过官方提供的、系统级的状态标志来声明我们的意图。InputDispatcher(负责分发所有触摸事件的底层服务)会尊重这些标志,并从源头上就阻止手势的发生。

解决方案

SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java

1. 添加需要屏蔽上滑功能的 Activity 合集

private final List<ComponentName> mHomeBlockingActivities = new ArrayList<>();

public TaskbarDelegate(Context context, LightBarTransitionsController.Factory lightBarTransitionsControllerFactory, StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
    mLightBarTransitionsControllerFactory = lightBarTransitionsControllerFactory;
    mContext = context;
    mDisplayManager = mContext.getSystemService(DisplayManager.class);
    mPipListener = (bounds) -> {
        mEdgeBackGestureHandler.setPipStashExclusionBounds(bounds);
    };
    mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
    mStatusBarKeyguardViewManager.setTaskbarDelegate(this);
    // 添加要屏蔽的 Activity
    String[] disabledActivities = mContext.getResources().getStringArray(R.array.config_blockedHomeGestureActivities);
    for (String disabledActivity : disabledActivities) {
        mHomeBlockingActivities.add(ComponentName.unflattenFromString(disabledActivity));
    }
}

在新建的 config_blockedHomeGestureActivities 数组中添加需要屏蔽类名即可

<string-array name="config_blockedHomeGestureActivities">
    <item>com.android.deskclock/.DeskClock</item>
</string-array>

2. 注册并监听任务栈变化

TaskbarDelegate 原本逻辑中就包含了 TaskStackChangeListener 的监听,但是没有 onTaskStackChanged 的重写,只需要加上即可

private boolean mIsHomeGestureBlocked;
private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
    @Override
    public void onLockTaskModeChanged(int mode) {
        mSysUiState.setFlag(SYSUI_STATE_SCREEN_PINNING, mode == LOCK_TASK_MODE_PINNED).commitUpdate(mDisplayId);
    }

    @Override
    public void onTaskStackChanged() {
        updateHomeGestureBlockState();
    }
};

private void updateHomeGestureBlockState() {
    boolean shouldBlock = false;
    ActivityManager.RunningTaskInfo runningTask = ActivityManagerWrapper.getInstance().getRunningTask();
    ComponentName topActivity = runningTask == null ? null : runningTask.topActivity;
    shouldBlock = topActivity != null && mHomeBlockingActivities.contains(topActivity);
    if (mIsHomeGestureBlocked != shouldBlock) {
        mIsHomeGestureBlocked = shouldBlock;
        updateSysuiFlags();
    }
}

3. 更新标志位 updateSysuiFlags

private void updateSysuiFlags() {
    android.util.Log.e("maxx", "updateSysuiFlags");
    int a11yFlags = mNavBarHelper.getA11yButtonState();
    boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
    boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
    
    // 新增修改:
    boolean overviewDisabled = (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0 || mIsHomeGestureBlocked;
    boolean homeDisabled = (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0 || mIsHomeGestureBlocked;
    
    mSysUiState.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable)
        .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
        .setFlag(SYSUI_STATE_IME_SHOWING, (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
        .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING, (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0)
        // 新增修改:
        .setFlag(SYSUI_STATE_OVERVIEW_DISABLED, overviewDisabled)
        .setFlag(SYSUI_STATE_HOME_DISABLED, homeDisabled)
        .setFlag(SYSUI_STATE_BACK_DISABLED, (mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)
        .setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isWindowVisible())
        .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY, allowSystemGestureIgnoringBarVisibility())
        .setFlag(SYSUI_STATE_IMMERSIVE_MODE, isImmersiveMode())
        .commitUpdate(mDisplayId);
}

还有一种思路是在 Launcher 做屏蔽处理,例如在 Launcher3 的源码中处理,在 TouchInteractionService 的 onInputEvent 方法中直接返回即可。

Launcher3/quickstep/src/com/android/quickstep/TouchInteractionService.java
public void onInputEvent(InputEvent ev) {
    if (needInterceptHome()) {
        return;
    }
    ...
}

目录

  1. 需求描述
  2. 需求分析
  3. 解决方案

更多推荐文章

查看全部
  • AI 赋能原则 5:当最聪明的大脑犯低级错误
  • Spring Boot 3.5.11 + JDK21 整合 RabbitMQ/RocketMQ/Kafka 对比与选型
  • 低空经济下无人机光伏巡检技术实践
  • LeetCode 202: 快乐数算法解析与实现
  • 网络安全从业者必备的十大学习资源与工具平台
  • C 语言数据结构:顺序表详解
  • 免费体验 Gemini 3 的 6 种方法
  • 基于 AI 的全栈开发新路径:自动生成 UI 设计稿与 H5 原型
  • Qwen-Multiple-Angles 插件实现图像多角度生成支持 ComfyUI 与 WebUI
  • AI Skills 入门:前端工程师如何构建可控 AI 系统
  • Ubuntu 22.04 安装 MySQL 5.7 完整指南
  • 命令行工具 MCPHost 实战:让大模型通过协议调用外部服务
  • PyAutoGUI Python 桌面自动化框架实战指南
  • OmniSteward:基于大语言模型的智能家居与电脑控制工具
  • 无线蜂窝网络:原理、架构与代际演进
  • Vue3 模板语法详解:插值、指令与响应式数据
  • OpenClaw Web Search 工具配置与渠道详解
  • B 站转型观察:从二次元社区到 AI 创新孵化器
  • PowerWiki:基于 Git 的知识管理系统
  • LFM2.5-1.2B-Thinking 模型效果展示与性能分析

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online