使用WebView文本select的自定义上下文操作栏

我已经使用了Google的这个指南,并且这个教程创build了我自己的上下文操作栏。

private ActionMode.Callback mActionModeCallback = new 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.annotation_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) { 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.custom_button: // do some stuff break; case R.id.custom_button2: // do some other stuff break; default: // This essentially acts as a catch statement // If none of the other cases are true, return false // because the action was not handled return false; } finish(); // An action was handled, so close the CAB return true; } // Called when the user exits the action mode @Override public void onDestroyActionMode(ActionMode mode) { mActionMode = null; } }; 

此菜单devise为在用户select文本时显示,因此覆盖原生复制/粘贴菜单。 现在我解决我的问题。

由于我重写了文本select的function,因此我还将一个LongClickListener添加到WebView并实现onLongClick(View v)方法,以便我可以检测用户何时进行select。

  myWebView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { if (mActionMode != null) { return false; } mActionMode = startActionMode(mActionModeCallback); v.setSelected(true); return true; } }); 

当我长按一下,我看到我的自定义菜单出现,但没有文字突出显示。
我需要有文本selectfunction; 没有它,我的菜单是毫无意义的。

如何覆盖onLongClick(View v) ,但保持Android提供的文本select?
如果这是不可能的,我可以打电话给startActionMode(mActionModeCallback)其他地方,以便文本将被选为正常,但我的自定义菜单也会出现?
如果这两者都不可能…帮助。

有一个更简单的方法! 见下面的更新:D


为了完整性,这里是我如何解决这个问题:

我根据这个答案跟随了这个build议,稍微调整一下,以便更接近地匹配重写的代码:

 public class MyWebView extends WebView { private ActionMode mActionMode; private mActionMode.Callback mActionModeCallback; @Override public ActionMode startActionMode(Callback callback) { ViewParent parent = getParent(); if (parent == null) { return null; } mActionModeCallback = new CustomActionModeCallback(); return parent.startActionModeForChild(this, mActionModeCallback); } } 

实质上,这会强制您的自定义CAB出现而不是Android CAB。 现在,您必须修改您的回叫,以便文本突出显示将与CAB一起消失:

 public class MyWebView extends WebView { ... private class CustomActionModeCallback implements ActionMode.Callback { ... // Everything up to this point is the same as in the question // Called when the user exits the action mode @Override public void onDestroyActionMode(ActionMode mode) { clearFocus(); // This is the new code to remove the text highlight mActionMode = null; } } } 

这就是它的全部。 请注意,只要您使用带重写的startActionMode MyWebView ,就没有办法获取本机CAB(复制/粘贴菜单,如果是WebView)。 实现这种行为可能是可能的,但这不是这种代码的工作方式。


更新:有一个更简单的方法来做到这一点! 上述解决scheme运作良好,但这是一个替代scheme,更简单的方法。

此解决scheme对ActionMode控制ActionMode ,但与上述解决scheme相比,所需代码less得多。

 public class MyActivity extends Activity { private ActionMode mActionMode = null; @Override public void onActionModeStarted(ActionMode mode) { if (mActionMode == null) { mActionMode = mode; Menu menu = mode.getMenu(); // Remove the default menu items (select all, copy, paste, search) menu.clear(); // If you want to keep any of the defaults, // remove the items you don't want individually: // menu.removeItem(android.R.id.[id_of_item_to_remove]) // Inflate your own menu items mode.getMenuInflater().inflate(R.menu.my_custom_menu, menu); } super.onActionModeStarted(mode); } // This method is what you should set as your item's onClick // <item android:onClick="onContextualMenuItemClicked" /> public void onContextualMenuItemClicked(MenuItem item) { switch (item.getItemId()) { case R.id.example_item_1: // do some stuff break; case R.id.example_item_2: // do some different stuff break; default: // ... break; } // This will likely always be true, but check it anyway, just in case if (mActionMode != null) { mActionMode.finish(); } } @Override public void onActionModeFinished(ActionMode mode) { mActionMode = null; super.onActionModeFinished(mode); } } 

这里是一个菜单,让你开始:

 <!-- my_custom_menu.xml --> <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/example_item_1" android:icon="@drawable/ic_menu_example_1" android:showAsAction="always" android:onClick="onContextualMenuItemClicked" android:title="@string/example_1"> </item> <item android:id="@+id/example_item_2" android:icon="@drawable/ic_menu_example_2" android:showAsAction="ifRoom" android:onClick="onContextualMenuItemClicked" android:title="@string/example_2"> </item> </menu> 

而已! 你完成了! 现在你的自定义菜单会显示出来,你不必担心select,你几乎不必担心ActionMode生命周期。

这对于占用其整个父ActivityWebView几乎是完美无缺的。 如果同时在您的Activity中存在多个View我不确定它将如何工作。 在这种情况下,这可能需要一些调整。

我做类似的方式是只覆盖onTouchListener,并调用GestureDetector来检测WebView是否被长时间按下,并从那里做我想做的事情。 以下是一些示例代码,可让您在不牺牲WebView中的文本select的情况下捕捉长按事件。 希望这有助于。

 @Override protected void onCreate(Bundle savedInstanceState) { WebView mWebView = (WebView) findViewById(R.id.myWebView); GestureDetector mGestureDetector = new GestureDetector(this, new CustomGestureListener()); mWebView.setOnTouchListener(new OnTouchListener(){ @Override public boolean onTouch(View view, MotionEvent arg1) { //Suggestion #1 - this just lets the touch to be handled by the system but allows you to detect long presses mGestureDetector.onTouchEvent(arg1); return false; //Suggestion #2 - this code will only let the touch be handled by the system if you don't detect a long press return mGestureDetector.onTouchEvent(arg1); } }); } private class CustomGestureListener extends SimpleOnGestureListener { @Override public void onLongPress(MotionEvent e) { //do stuff } }