Android 12:从 Editor 到 FloatingToolbar 全局收敛长按菜单

接上一篇在 ActivityStarter 侧拦截分享类 Activity 的做法:即便能挡住最终界面,客户仍希望 在 EditText、TextView、WebView 等长按文本时,弹出的浮动菜单里就不要出现「分享」「打开」「网页搜索」 ,避免用户习惯从菜单侧发起跳转。仅对单个应用设置 setCustomSelectionActionModeCallback 无法覆盖「所有应用」,因此需要在 Framework 层分两路处理:普通文本控件走 Editor,WebView 文本选择浮动条走 FloatingToolbar

为什么应用层方案不够用

网上常见示例是在应用里为 EditText 注册 setCustomSelectionActionModeCallback,自定义 ActionMode 菜单。该方式只能约束 自己写的界面,无法约束系统应用、第三方应用以及 WebView 内核菜单,因此本方案直接改 AOSP 文本编辑与浮动工具栏实现。


一、EditText / TextView:修改 Editor.java

路径: frameworks/base/core/java/android/widget/Editor.java

「分享」项由文本选择的 ActionMode / 上下文菜单 创建,与 TextView.ID_SHAREMENU_ITEM_ORDER_SHARE 等常量相关。做法有两类(与你们实际补丁一致即可):

在构建上下文菜单时,原逻辑会向 menu 注册 ID_SHARE。将整块注释掉,并加上说明注释,例如:

java 复制代码
// 屏蔽搜索(分享入口)
// menu.add(Menu.NONE, TextView.ID_SHARE, MENU_ITEM_ORDER_SHARE,
//         com.android.internal.R.string.share)
//         .setEnabled(mTextView.canShare())
//         .setOnMenuItemClickListener(mOnContextMenuClickListener);
menu.add(Menu.NONE, TextView.ID_SELECT_ALL, MENU_ITEM_ORDER_SELECT_ALL,
        com.android.internal.R.string.selectAll)

(中间保留粘贴、全选、自动填充等你们需要保留的项。)

在根据 canPaste() / canShare() 等向 Menu 添加条目时,将「分享」整段用块注释关闭,避免长按后工具栏仍出现分享:

java 复制代码
if (mTextView.canPaste()) {
    menu.add(Menu.NONE, TextView.ID_PASTE, MENU_ITEM_ORDER_PASTE,
            com.android.internal.R.string.paste)
            .setAlphabeticShortcut('v')
            .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}

/* // 屏蔽长按文本弹出来的分享按钮
if (mTextView.canShare()) {
    menu.add(Menu.NONE, TextView.ID_SHARE, MENU_ITEM_ORDER_SHARE,
            com.android.internal.R.string.share)
            .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
*/

if (mTextView.canRequestAutofill()) {
    final String selected = mTextView.getSelectedText();
    if (selected == null || selected.isEmpty()) {
        menu.add(Menu.NONE, TextView.ID_AUTOFILL, MENU_ITEM_ORDER_AUTOFILL,
                com.android.internal.R.string.autofill)
                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    }
}

注意: 注释掉分享后,若还有「网页搜索」等来自 WebView 内部浮动条 的项,需继续看第二节;纯 TextView 场景一般与 Editor 同步收敛即可。


二、WebView:在 FloatingToolbar.java 中过滤菜单

路径: frameworks/base/core/java/com/android/internal/widget/FloatingToolbar.java

调试可知:WebView 长按选中文本后出现的「打开」「分享」「网页搜索」等,多由 FloatingToolbar 在展示前组装菜单。在 doShow() 展示浮动条之前插入一次过滤,只保留复制、粘贴、全选、剪切等系统字符串标题对应的项,其余一律 setVisible(false)

1. 在 doShow() 开头调用过滤

java 复制代码
private void doShow() {
    removeRedundantMenu(mMenu); // 过滤掉不需要的弹出菜单
    List<MenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu);
    menuItems.sort(mMenuItemComparator);
    // ... 后续 layout / show 逻辑不变
}

2. 新增 removeRedundantMenu 方法(示例)

通过标题与系统字符串资源比对,只保留基础编辑能力,隐藏「打开」「分享」「网页搜索」等扩展项:

java 复制代码
private void removeRedundantMenu(Menu menu) {
    int size = menu.size();
    for (int i = 0; i < size; i++) {
        MenuItem item = menu.getItem(i);
        if (mContext.getResources().getString(android.R.string.copy).equals(item.getTitle())
                || mContext.getResources().getString(android.R.string.paste).equals(item.getTitle())
                || mContext.getResources().getString(android.R.string.selectAll).equals(item.getTitle())
                || mContext.getResources().getString(android.R.string.cut).equals(item.getTitle())) {
            // 保留复制、粘贴、全选、剪切
        } else {
            item.setVisible(false);  // 隐藏其余菜单项
        }
    }
}

说明:

  • 若某些 ROM 或 WebView 版本把「打开」等做成了与系统字符串不完全一致的标题,需要再按 item.getItemId()title 关键字补充白名单/黑名单。
  • 过滤后若出现空菜单或异常,需在真机上覆盖 Chrome WebView、系统 WebView 与典型业务 H5 页回归。

三、联调与边界

层级 作用范围 典型风险
Editor 系统 TextView/EditText 系长按 需确认 SearchView、自定义 Editor 是否另有入口
FloatingToolbar WebView 等使用浮动工具栏的场景 不同语言下 title 可能本地化,需抽样验证
与上一篇配合 ActivityStarter 兜底 即使菜单漏出,仍可拦截最终 Activity
相关推荐
Mr YiRan2 小时前
网络请求API监控与网络切换埋点
android·网络
美狐美颜sdk6 小时前
直播APP开发技术栈详解:视频美颜SDK、人脸识别与实时渲染
android·人工智能·音视频·美颜sdk·直播美颜sdk
奈斯先生Vector11 小时前
当模型版本不断变化,RelayRouter 能否帮助 AI 应用摆脱深度绑定
android·java·人工智能·开源·aigc
TimeFine11 小时前
让强模型做“总工”,让高性价比模型写代码
android
又见情义13 小时前
RK3568 Android 13 屏蔽 healthd 电池日志经验分享
android
古法安卓16 小时前
Android-Fork 机制详解
android·java·android studio
Android打工仔16 小时前
不要在 Data 层随意把 Cold Flow 转换成 Hot Flow
android·架构·kotlin
峥嵘life17 小时前
2026免费的 opencode 使用分享:Windows端 + 服务器CLI 实战总结
android·大数据·开发语言
杉氧17 小时前
拒绝重复造轮子:我写了一个生产级的 Kotlin 协程与 Flow 工具库(CoroutineKit)
android·kotlin·workflow
恋猫de小郭18 小时前
Flutter GSoC 2026 提案进度解读,补上 DevTools、FFI 和原生平台的关键缺口
android·前端·flutter