需求描述:
- 长按WebView出现Context menu,显示"复制”菜单
- 点击上述菜单后选择文本,复制到剪贴板
概要设计+详细设计:
- 用OnTouchListener实现长按实现(参照android.view.View)
- 实现WebView的Context menu(在Activity实例中实现)
- 实现复制文本功能(兼容多个sdk)
编码:
public class WebViewCopy { private Activity activity; private WebView webview; private static boolean mIsSelectingText; public static final String TAG=WebViewCopy.class.getSimpleName(); public WebViewCopy(final Activity activity, final WebView webView){ this.webview=webView; this.activity=activity; this.activity.registerForContextMenu(this.webview); webView.requestFocus(View.FOCUS_DOWN); webView.setOnTouchListener(new OnTouchListener() { boolean mHasPerformedLongPress; Runnable mPendingCheckForLongPress; @Override public boolean onTouch(final View v, MotionEvent event) { /* webView.getSettings().setBuiltInZoomControls(false); webView.getSettings().setSupportZoom(false);*/ Log.d(TAG, "event:" + event.getAction()); switch (event.getAction()) { case MotionEvent.ACTION_UP: mIsSelectingText = false; if (!v.hasFocus()) { v.requestFocus(); } if (!mHasPerformedLongPress) { // This is a tap, so remove the longpress check if (mPendingCheckForLongPress != null) { v.removeCallbacks(mPendingCheckForLongPress); } // v.performClick(); } break; case MotionEvent.ACTION_DOWN: if (!v.hasFocus()) { v.requestFocus(); } if( mPendingCheckForLongPress == null) { mPendingCheckForLongPress = new Runnable() { public void run() { //((WebView)v).performLongClick(); if(! mIsSelectingText) { activity.openContextMenu(webview); mHasPerformedLongPress = true; mIsSelectingText = false; } } }; } mHasPerformedLongPress = false; v. postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout()); break; case MotionEvent.ACTION_MOVE: final int x = (int) event.getX(); final int y = (int) event.getY(); // Be lenient about moving outside of buttons int slop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop(); if ((x < 0 - slop) || (x >= v.getWidth() + slop) || (y < 0 - slop) || (y >= v.getHeight() + slop)) { if (mPendingCheckForLongPress != null) { v. removeCallbacks(mPendingCheckForLongPress); } } break; default: return false; } return false; } }); } public static synchronized void emulateShiftHeld(WebView view) { try { KeyEvent shiftPressEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SHIFT_LEFT, 0, 0); shiftPressEvent.dispatch(view); } catch (Exception e) { Log.e(TAG, "Exception in emulateShiftHeld()", e); } } public synchronized void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo,final int copy,String menuString) { MenuItem menuitem=menu.add(1, copy, Menu.NONE, menuString); menuitem.setOnMenuItemClickListener(new OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { if(item.getItemId()==copy){ //emulateShiftHeld(webview); selectAndCopyText(webview); } return false; } }); } public static synchronized void selectAndCopyText(WebView v) { try { mIsSelectingText = true; //Method m = WebView.class.getMethod("emulateShiftHeld", Boolean.TYPE); // m.invoke(v, false); if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.ECLAIR) { Method m = WebView.class.getMethod("emulateShiftHeld", Boolean.TYPE); m.invoke(v, false); } else { Method m = WebView.class.getMethod("emulateShiftHeld"); m.invoke(v); } } catch (Exception e) { // fallback emulateShiftHeld(v); }finally{ //Toast.makeText(activity, "Select text", Toast.LENGTH_SHORT).show(); } } }
下面的代码在activity中写:
1) 在onCreate中生成 WebViewCopy 实例: copy = new WebViewCopy(this, _webView);
2) 在onCreateContextMenu中注入复制菜单public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) { copy.onCreateContextMenu(menu, v, menuInfo,COPY,getString(R.string.copy)); super.onCreateContextMenu(menu, v, menuInfo); }
回顾与总结:
OnTouchListener可能在一些时候更本不响应,如Zoom Button出现后。这得让WebView重新获取焦点,
这是WebView又一已知的Bug. 整个难点在于重新获取焦点: webview.requestFocus();
参考: http://code.google.com/p/android/issues/detail?id=7189