当前位置: 首页 > 知识库问答 >
问题:

javascript - 为何Android WebView 调用JS 扩选文本导致长按菜单不出现?

柏夕
2024-01-20

Android WebView 使用JS扩选的问题:我的需求是当用户选择webview的文本内容的时候,首次的长按的时候帮助用户选择整段,后续用户扩选的时候,自动帮用户选择整句。

我的思路是当原生这边创建系统菜单的时候(也就是用户开始选择之后),调用js,通过js的Selection API去做一个扩选。比如使用了下面的代码:

function modify() {  function forwardWord(selection) {    var focusNode = selection.focusNode;    var range = selection.getRangeAt(0);    range.setEnd(range.endContainer, focusNode.textContent.length);    selection.addRange(range);  }  let selection = window.getSelection();  selection.modify("extend", "backward", "paragraphboundary");  forwardWord(selection);  console.log("selection.toString()", selection.toString());  return selection.toString();}

调用了该Js之后,发现是能扩选成功的,但是系统的菜单就不再出现了,光标也不展示了。

其中,系统菜单是在长按的时候出现的那些例如:复制,全选,分享之类的系统自带的。

我是通过重写WebView的startActionMode(ActionMode.Callback callback)startActionMode(ActionMode.Callback callback, int type)方法,创建自己的Callback代理,例如:

fun proxyWebMenuCallback(callback: ActionMode.Callback, noticeH5: () -> Unit): ActionMode.Callback {  return if (SdkVersion.maxThanM()) {    WebViewMenuCallback2(callback, noticeH5)  } else {    WebViewMenuCallback(callback, noticeH5)  }}@SuppressLint("NewApi")class WebViewMenuCallback2(  private val callback: ActionMode.Callback,  private val noticeH5: () -> Unit) : ActionMode.Callback2() {  override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {    return callback.onCreateActionMode(mode, menu).also {      LogUtil.d("WebViewMenuCallback2", "onCreateActionMode:$it")    }  }  override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {    noticeH5()    return callback.onPrepareActionMode(mode, menu).also {      LogUtil.d("WebViewMenuCallback2", "onPrepareActionMode:$it")    }  }  override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {    return callback.onActionItemClicked(mode, item).also {      LogUtil.d("WebViewMenuCallback2", "onActionItemClicked:$it")    }  }  override fun onDestroyActionMode(mode: ActionMode?) {    callback.onDestroyActionMode(mode).also {      LogUtil.d("WebViewMenuCallback2", "onDestroyActionMode:$it")    }  }  override fun onGetContentRect(mode: ActionMode?, view: View?, outRect: Rect?) {    if (callback is ActionMode.Callback2) {      callback.onGetContentRect(mode, view, outRect)    } else {      super.onGetContentRect(mode, view, outRect)    }  }}class WebViewMenuCallback(  private val callback: ActionMode.Callback,  private val noticeH5: () -> Unit) : ActionMode.Callback {  override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {    return callback.onCreateActionMode(mode, menu)  }  override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {    noticeH5()    return callback.onPrepareActionMode(mode, menu)  }  override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {    return callback.onActionItemClicked(mode, item)  }  override fun onDestroyActionMode(mode: ActionMode?) {    callback.onDestroyActionMode(mode)  }}

我的想法是在创建菜单的监听中去执行JS代码进行扩选,但是无论我把noticeH5回调放在onPrepareActionMode还是onCreateActionMode中调用,还是noticeH5的实现是会通过postDelay去调用JS,结果都是扩选成功,但是菜单却被隐藏了,光标不见了。
然后我尝试在执行JS完成之后,例如:

evaluateJavascript("javascript:" + js, value -> {                    postDelay(1000){                        //performLongClick();                        //showContextMenu();                    }                }        );

都不起效,无论是否一起调用。

后面我尝试在自定义菜单也不可以。
还尝试了自定义菜单,然后在执行完成JS之后,调用startActionMode方法,发现是会出现菜单的,但是菜单的位置出现不正确,而且光标也不见了,同时该方法还只能去设置执行一次,否则会陷入死循环,因为调用该方法之后,Callback里面的方法又开始执行了。

最后尝试了全部逻辑交由JS实现,结果就是js的菜单位置,扩选啥的都能直接做到,但是却不能自由选择了,光标没有了。

我的想法是这是否是Chrome的Bug,即在JS扩展选区的时候不能同步到原生端?因为扩选选区之后是会回调Callback的onGetContentRect方法的,我还在给官方提了bug,但是暂时也没有回应。When JavaScript expand the selection,WebView ContextMenu aoto dimiss

请问应该如何实现JS扩展选区,扩选之后菜单能保持在正确的位置。

PS: 小米浏览器有一个扩选的功能,而且扩选之后是可以正常展示菜单和光标的。或者有知道小米的是如何实现的吗?

共有1个答案

蔺沛
2024-01-20

最终是修改了方案实现,用户可以自由复制,端上通过自定义Callback实现全自定义菜单。通过开源方案选择完整句子get-selection-more,对开源的方案进行了优化,增加了换行补充。

 类似资料:
  • 本文向大家介绍iOS中长按调出菜单组件UIMenuController的使用实例,包括了iOS中长按调出菜单组件UIMenuController的使用实例的使用技巧和注意事项,需要的朋友参考一下 UIMenuController的使用    UIMenuController的展现需要基于一个View视图,其交互则需要基于其所在View视图的Responder。举例来说,如果一个UIMenuCont

  • 本文向大家介绍Android 实现长按弹出PopupMenu 菜单栏,包括了Android 实现长按弹出PopupMenu 菜单栏的使用技巧和注意事项,需要的朋友参考一下 在Android中的SDK3.0版本以后加入了一个特殊的菜单效果,它可以在任何的View上显示,根据View的位置显示菜单效果。 res/menu/menu.xml MainActivity.java 总结 以上所述是小编给大家

  • js 文本划选 第一次划取执行的时候没有问题,获取的起始位置是相对于div标签的。 执行之后会向html中添加span标签 第二次划取span标签后的文字的时候,获取的起始位置却是相对于span标签的 有没有方法每次获取的起始位置都是相对于div的?

  • 问题内容: 我想禁用长按(触摸并按住)Web应用程序中的图像后出现的上下文菜单。我见过关于如何做到这一点的不同想法,但似乎没有一个对我有用。 有没有办法通过HTML / CSS / Javascript在Android上执行此操作? 问题答案: 这应该适用于1.6或更高版本(如果我没记错的话)。我不认为有1.5或更早版本的解决方法。

  • 我想在单击导航抽屉时更改其文本颜色。 我怎么能做到呢? 这是我的清单:

  • 使用任何按钮都可以触发一个弹出菜单,只需要把它放置在一个.btn-group中,并提供适当的弹出菜单标记。 插件依赖 按钮弹出菜单需要你的Bootstrap中调用了弹出菜单插件。 内容 单按钮弹出菜单 通过一些基本的标记变化,将一个按钮变成一个弹出菜单。 <!-- Single button --> <div class="btn-group"> <button type="button"