我在ActionMode.Callback
内为自定义文字选择功能实施了WebView
。我遇到的问题是选择和操作模式状态不匹配。
当我长按时,一切都很好。
当我与其中一个按钮或WebView
(不包括实际选择)进行交互时,ActionMode
应该被销毁,选择应该消失。
在Android 4.4,KitKat中,这正是发生的事情。
然而,这不是4.1.1 - 4.3,Jelly Bean中发生的事情。当我单击其中一个按钮时,不会删除选择。
当我在选择之外点击时,恰好相反。选择将被删除,但上下文操作栏仍保留在屏幕上。
<小时/> 以下是我的
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来说并不陌生;它自API 1以来一直在Android中。)
如何解决这个问题?
答案 0 :(得分:7)
经过多次尝试解决这个问题,我终于明白了!
重要的是要知道Android 4.4(KitKat)之前的WebView
与典型的浏览器不同。有一些隐藏的类可以开始搞乱事情。有WebViewCore
可以完成所有繁重的工作并实际产生结果,并且WebViewClassic
是这个问题的罪魁祸首。
解决方案是半黑客攻击,因为你不需要做任何操作来操作底层类,但你必须抓住问题场景。
WebViewClassic
负责拦截长按并处理它们以进行文本选择,包括选择突出显示和选择控制柄的动画,以及启动填充上下文操作栏的ActionMode
(出租车)。不幸的是,由于我们希望用我们自己的方式覆盖ActionMode
,因此文本选择和CAB变得不同步,因为它们彼此不相关。要解决此问题,请跟踪您自己的自定义ActionMode.Callback
,以及与选择动画相关联的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
并收听单击事件。当我收到该活动时,我会在finish()
上致电mActionMode
关闭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);
}
}
<小时/> 这应该做到!我们做到了!
在其他任何地方都找不到WebViewClassic
素材;这就是为什么我提供了很多关于发生了什么的细节。调试器花了很多时间来弄清楚发生了什么。幸运的是,GestureDetector
类已有详细记录,并包含多个教程。我从Android Developers website获取了我的信息。我希望这能帮助那些像我一样努力解决这个问题的人。 :)