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

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.5k
1 个回答

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

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏