我有一个的WebView
中实现的 ActionMode.Callback
自定义文本选择功能。那我遇到的问题是,选择和动作模式状态不匹配。
I have implemented an ActionMode.Callback
for custom text selection functions within a WebView
. The problem that I am having is that the selection and the action mode states do not match.
当我长preSS,一切都开始出来就好了。
When I long-press, everything starts out just fine.
当我与一个按钮,或的WebView
(不包括实际的选择)交互则 ActionMode
应销毁,并选择将消失。
When I interact with one of the buttons, or the WebView
(excluding the actual selection) then the ActionMode
should be destroyed, and the selection should disappear.
在Android 4.4系统,奇巧,这正是发生了什么。
In Android 4.4, KitKat, this is exactly what happens.
不过,这是不是发生什么事4.1.1 - 4.3果冻豆。当我点击一个按钮,选择不会被删除。
However, this is not what is happening in 4.1.1 - 4.3, Jelly Bean. When I click one of the buttons, the selection is not removed.
当我点击选区外,正好相反的情况。选择被删除,但上下文操作栏保留在屏幕上。
When I tap outside the selection, just the opposite happens. The selection is removed, but the contextual action bar remains on the screen.
这里是code我的 CustomWebView
Here is the code for my 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()
方法。当我删除的方法,pressing一个按钮离开,选择4.4的背后,就像果冻豆的行为。 clearFocus()
给出了4.4的预期的行为,但不转移到更早的API。 (请注意, clearFocus()
是不是新的奇巧;因为API 1它已经在安卓)
As the comment above shows, I believe the problem is with the clearFocus()
method. When I remove that method, pressing a button leaves the selection behind in 4.4, just like the behavior in Jelly Bean. clearFocus()
gives the expected behavior in 4.4, but is not transferring to earlier APIs. (Do note that clearFocus()
is not new to KitKat; it has been in Android since API 1.)
这怎么能解决吗?
推荐答案
经过无数次的尝试解决这个问题,我终于明白了!
After numerous attempts at solving this, I have finally got it!
要认识到的重要一点是,的WebView
的Android 4.4(奇巧)之前,是从你的典型的浏览器不同。有几个隐藏的类,开始发挥作用,启动弄乱的东西。有 WebViewCore
这做所有繁重的任务和实际产生的结果,并有 WebViewClassic
,这是这一问题的罪魁祸首
The important thing to realize is that WebView
s before Android 4.4 (KitKat) are different from your typical browser. There are a few hidden classes that come into play that start to mess up things. There's WebViewCore
which does all the heavy lifting and actually produces results, and there's WebViewClassic
, which is the culprit of this problem.
该解决方案是一个半劈,因为你真的没有做任何事情来操纵底层类,但你必须抓住问题的方案。
The solution is a semi-hack, as you don't really have to do anything to manipulate the underlying classes, but you do have to catch the problem scenarios.
WebViewClassic
需要截取长presses和处理他们的文本选择,包括选择高亮的动画和选择手柄,以及开始照顾 ActionMode
的填充上下文操作栏(CAB)。不幸的是,因为我们要覆盖 ActionMode
用我们自己的,文本选择和CAB不同步的,因为他们没有相互关联。为了解决这个问题,你自己的自定义 ActionMode.Callback
的跟踪,以及的 ActionMode.Callback
与选择动画相关联。然后,当你的 ActionMode.Callback
被破坏,调用选择的完成()
方法来销毁 ActionMode.Callback
,以及
WebViewClassic
takes care of intercepting long presses and handling them for text selection, including the animation of the selection highlight and the selection handles, as well as starting the ActionMode
that populates the Contextual Action Bar (CAB). Unfortunately, since we want to override that ActionMode
with our own, the text selection and the CAB become out of sync, because they are not associated with each other. To solve this, keep track of your own custom ActionMode.Callback
, as well as the ActionMode.Callback
associated with the selection animation. Then, when your ActionMode.Callback
is destroyed, call the selection's finish()
method to destroy that ActionMode.Callback
, as well.
OK,少废话;这里的code。
OK, enough talk; here's the code.
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;
}
}
}
信不信由你,我们只是完成了一半。上述code采用去除选择CAB关闭时的照顾。要关闭CAB上的触摸事件,我们必须做一些更多的工作。这一次,它的更简单。我用的是 GestureDetector
并监听一个水龙头事件。当我得到这个事件,我称之为在
关闭CAB: mActionMode
完成()
Believe it or not, we're only halfway finished. The above code takes care of removing the selection when the CAB closes. To close the CAB on a touch event, we have to do a little more work. This time it's much more straightforward. I use a GestureDetector
and listen for a single tap event. When I get that event, I call finish()
on mActionMode
to close the 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);
}
}
这应该这样做!我们做到了!
And that should do it! We did it!
您将无法找到 WebViewClassic
料其他地方;这就是为什么我提供了这么多的细节来发生了什么。它花了很多时间与调试器弄清楚发生了什么事情。幸运的是, GestureDetector
类是证据充分的,并包括多个教程。我从 Android开发者网站我的信息。我希望这有助于那些你这个问题,我一样挣扎着。 :)
You will not find the WebViewClassic
material anywhere else; that's why I provided so much detail as to what's happening. It took many hours with the debugger to figure out what was going on. Fortunately, the GestureDetector
class is well-documented, and includes multiple tutorials. I got my information from the Android Developers website. I hope this helped those of you that struggled with this problem as much as I did. :)
相关推荐
最新文章