Webview文本选择不清除

我已经在WebView为自定义文本选择函数实现了ActionMode.Callback 。 我遇到的问题是选择和操作模式状态不匹配。

当我长按时,一切都开始了。

长按后,选择突出显示将与上下文操作栏一起显示。

当我与其中一个按钮或WebView (不包括实际选择)进行ActionMode应该销毁ActionMode ,并且选择应该消失。

选择按钮,或触摸选择区域外。CAB应该关闭,选择应该消失。

在Android 4.4,KitKat中,这正是发生的事情。


然而,这不是4.1.1 – 4.3,Jelly Bean中发生的情况。 单击其中一个按钮时,不会删除选择。

点击上下文操作栏中的按钮。点击按钮后,选择仍然存在。

当我在选择之外点击时,恰好相反。 选择被删除,但上下文操作栏仍保留在屏幕上。

<img src="http://img.androidcookie.com/android/fPlrTm.jpg" alt="选择开始后点击 WebView 。”>选择被清除,但上下文操作栏仍然存在。


这是我的CustomWebView的代码

 public class CustomWebView extends WebView { private ActionMode.Callback mActionModeCallback; @Override public ActionMode startActionMode(Callback callback) { ViewParent parent = getParent(); if (parent == null) { return null; } mActionModeCallback = new CustomActionModeCallback(); return parent.startActionModeForChild(this, mActionModeCallback); } private class CustomActionModeCallback implements ActionMode.Callback { // Called when the action mode is created; startActionMode() was called @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { // Inflate a menu resource providing context menu items MenuInflater inflater = mode.getMenuInflater(); inflater.inflate(R.menu.contextual_menu, menu); return true; } // Called each time the action mode is shown. // Always called after onCreateActionMode, but // may be called multiple times if the mode is invalidated. @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { // This method is called when the handlebars are moved. loadJavascript("javascript:getSelectedTextInfo()"); return false; // Return false if nothing is done } // Called when the user selects a contextual menu item @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch(item.getItemId() { case R.id.button_1: // do stuff break; ... default: break; } mode.finish(); // Action picked, so close the CAB return true; } // Called when the user exits the action mode @Override public void onDestroyActionMode(ActionMode mode) { // TODO This does not work in Jelly Bean (API 16 - 18; 4.1.1 - 4.3). clearFocus(); // Remove the selection highlight and handles. } } } 

正如上面的评论所示,我认为问题在于clearFocus()方法。 当我删除该方法时,按下按钮会使选择落后于4.4,就像Jelly Bean中的行为一样。 clearFocus()在4.4中给出了预期的行为,但没有转移到早期的API。 (请注意, clearFocus()对于KitKat来说并不clearFocus() ;它自API 1以来一直在Android中。)

怎么解决这个问题?

经过多次尝试解决这个问题,我终于明白了!

重要的是要知道Android 4.4(KitKat)之前的WebView与典型的浏览器不同。 有一些隐藏的类可以开始搞乱事情。 WebViewCore可以完成所有繁重的工作并实际产生结果,还有WebViewClassic ,这是这个问题的罪魁祸首。

解决方案是半破解,因为您实际上不需要做任何操作来操作底层类,但您必须抓住问题场景。

WebViewClassic负责拦截长按并处理它们以进行文本选择,包括选择突出显示和选择控制柄的animation,以及启动填充上下文操作栏(CAB)的ActionMode 。 不幸的是,由于我们想要使用我们自己的ActionMode覆盖它,因此文本选择和CAB变得不同步,因为它们彼此不相关。 要解决此问题,请跟踪您自己的自定义ActionMode.Callback以及与选择animation关联的ActionMode.Callback 。 然后,当你的ActionMode.Callback被销毁时,调用选择的finish()方法来销毁该ActionMode.Callback

好的,说得够多; 这是代码。

 public class CustomWebView extends WebView { private ActionMode mActionMode; private ActionMode.Callback mActionModeCallback; // Add this class variable private ActionMode.Callback mSelectActionModeCallback; @Override public ActionMode startActionMode(Callback callback) { /* When running Ice Cream Sandwich (4.0) or Jelly Bean (4.1 - 4.3), there * is a hidden class called 'WebViewClassic' that draws the selection. * In order to clear the selection, save the callback from Classic * so it can be destroyed later. */ // Check the class name because WebViewClassic.SelectActionModeCallback // is not public API. String name = callback.getClass().toString(); if (name.contains("SelectActionModeCallback")) { mSelectActionModeCallback = callback; } mActionModeCallback = new CustomActionModeCallback(); // We haven't actually done anything yet. Send our custom callback // to the superclass so it will be shown on screen. return super.startActionModeForChild(this, mActionModeCallback); } private class CustomActionModeCallback implements ActionMode.Callback { // Called when the action mode is created; startActionMode() was called @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { // This is important for part 2. mActionMode = mode; // Inflate a menu resource providing context menu items MenuInflater inflater = mode.getMenuInflater(); inflater.inflate(R.menu.contextual_menu, menu); return true; } // Called each time the action mode is shown. // Always called after onCreateActionMode, but // may be called multiple times if the mode is invalidated. @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { // This method is called when the handlebars are moved. loadJavascript("javascript:getSelectedTextInfo()"); return false; // Return false if nothing is done } // Called when the user selects a contextual menu item @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch(item.getItemId() { case R.id.button_1: // do stuff break; ... default: break; } mode.finish(); // Action picked, so close the CAB return true; } // Called when the user exits the action mode @Override public void onDestroyActionMode(ActionMode mode) { clearFocus(); // Remove the selection highlight and handles. // Semi-hack in order to clear the selection // when running Android earlier than KitKat. if (mSelectActionModeCallback != null) { mSelectActionModeCallback.onDestroyActionMode(mode); } // Relevant to part 2. mActionMode = null; } } } 

信不信由你,我们只完成了一半。 上面的代码负责在CAB关闭时删除选择。 要在触摸事件中关闭CAB,我们必须做更多的工作。 这一次,它更加直截了当。 我使用GestureDetector并监听单击事件。 当我得到那个事件时,我在mActionMode上调用finish()来关闭CAB:

 public class CustomWebView extends WebView { private ActionMode mActionMode; private ActionMode.Callback mActionModeCallback; private ActionMode.Callback mSelectActionModeCallback; // Code from above segment ... private class CustomGestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent e) { if (mActionMode != null) { mActionMode.finish(); return true; } return false; } } @Override public boolean onTouchEvent(MotionEvent event) { // Send the event to our gesture detector // If it is implemented, there will be a return value this.mDetector.onTouchEvent(event); // If the detected gesture is unimplemented, send it to the superclass return super.onTouchEvent(event); } } 

这应该做到! 我们做到了!

您将无法在其他任何地方findWebViewClassic材料; 这就是为什么我提供了很多关于发生了什么的细节。 调试器花了很多时间来弄清楚发生了什么。 幸运的是, GestureDetectorGestureDetector记录,并包含多个教程。 我从Android开发者网站上获取了我的信息。 我希望这能帮助那些像我一样努力解决这个问题的人。 🙂