Skip to content

x5 WebView自定义长按菜单

想要自定义长按菜单,重写startActionMode的做法在x5 WebView上并不起作用。

x5 WebView底层并没有走startActionMode方法。我们需要换个思路。 这里用menu来自定义菜单。

先新建一个menu文件web_view_x5_menu1.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/copy"
        android:title="Copy"
        app:showAsAction="always" />
    <item
        android:id="@+id/action1"
        android:title="action1"
        app:showAsAction="always" />
</menu>

操作x5 WebView

新建一个类继承ProxyWebViewClientExtension,实现onShowLongClickPopupMenu方法。 长按弹出菜单时,会走这个方法。

新建ISelectionInterfaceX类实现ISelectionInterface接口。 updateHelperWidget方法里对菜单进行操作。把默认的菜单换成我们的自定义菜单。

    private class ProxyWebViewClientExtensionX extends ProxyWebViewClientExtension {
        private WebView webView;
        private Handler handler = new Handler(Looper.getMainLooper());

        public ProxyWebViewClientExtensionX(WebView webView) {
            this.webView = webView;
        }

        @Override
        public boolean onShowLongClickPopupMenu() {
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    webView.getX5WebViewExtension().enterSelectionMode(false);
                }
            }, 30);
            return true;
        }
    }

    private class ISelectionInterfaceX implements ISelectionInterface {
        boolean isInActionMode = false;
        ActionMode actionMode;
        private WebView webView;
        View actionView;
        private Handler handler = new Handler(Looper.getMainLooper());

        public ISelectionInterfaceX(WebView webView) {
            this.webView = webView;
        }

        @Override
        public void onSelectionChange(Rect rect, Rect rect1, int i, int i1, short i2) {
            Log.d(TAG, "onSelectionChange: ");
        }

        @Override
        public void onSelectionBegin(Rect rect, Rect rect1, int i, int i1, short i2) {
            Log.d(TAG, "onSelectionBegin: ");
        }

        @Override
        public void onSelectionBeginFailed(int i, int i1) {
            Log.d(TAG, "onSelectionBeginFailed: ");
        }

        @Override
        public void onSelectionDone(Rect rect, boolean b) {
            Log.d(TAG, "onSelectionDone: ");
        }

        @Override
        public void hideSelectionView() {
            System.out.println();
            if (actionView != null) {
                webView.removeViewInLayout(actionView);
                actionView = null;
            }
            if (actionMode != null) {
                actionMode.finish();
                actionMode = null;
            }
        }

        @Override
        public void onSelectCancel() {
            Log.d(TAG, "onSelectCancel: ");
        }

        @Override
        public void updateHelperWidget(final Rect rect, final Rect rect1) {
            System.out.println();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    final ActionMode.Callback callback = new ActionMode.Callback() {
                        @Override
                        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                            mode.getMenuInflater().inflate(R.menu.web_view_x5_menu1, menu);
                            return true;
                        }

                        @Override
                        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                            return false;
                        }

                        @Override
                        public boolean onActionItemClicked(ActionMode mode,
                                                           MenuItem item) {
                            final IX5WebViewExtension webViewExtension = webView.getX5WebViewExtension();
                            String selectionText = "";
                            if (webViewExtension != null) {
                                selectionText = webViewExtension.getSelectionText();
                            }
                            boolean leaveSelection = true;
                            switch (item.getItemId()) {
                                case R.id.copy:
                                    Toast.makeText(WebViewX5ClickAct.this, selectionText, Toast.LENGTH_SHORT).show();
                                    break;
                                case R.id.action1:
                                    Toast.makeText(WebViewX5ClickAct.this, "(action1) " + selectionText, Toast.LENGTH_SHORT).show();
                                    break;
                            }
                            final boolean finalLeaveSelection = leaveSelection;
                            handler.postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    if (finalLeaveSelection && webViewExtension != null) {
                                        webViewExtension.leaveSelectionMode();
                                    }
                                }
                            }, 30);
                            return true;
                        }

                        @Override
                        public void onDestroyActionMode(ActionMode mode) {
                            isInActionMode = false;
                        }
                    };

                    Context context = webView.getContext();

                    // 生成actionView,并通过它启动ActionMode,解决定位问题
                    // actionView的大小和位置,大致和Selection相同,直接将其add到WebView中
                    if (actionView != null) {
                        webView.removeViewInLayout(actionView);
                    }

                    actionView = new View(context);
                    actionView.setBackgroundColor(0x33FF00FF);// 方便调试
                    int width = rect1.right - rect.left;
                    int height = rect1.bottom - rect.top;
                    FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(width <= 0 ? 10 : width, height <= 0 ? 10 : height);
                    lp.leftMargin = rect.left;
                    lp.topMargin = rect.top;
                    webView.addView(actionView, lp);

                    // 需要延迟startActionMode,给布局actionView的时间
                    actionView.post(new Runnable() {
                        @Override
                        public void run() {
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                actionMode = actionView.startActionMode(callback, ActionMode.TYPE_FLOATING);
                            } else {
                                actionMode = actionView.startActionMode(callback);
                            }
                        }
                    });
                }
            }, 0);
        }

        @Override
        public void setText(String s, boolean b) {
            Log.d(TAG, "setText: ");
        }

        @Override
        public String getText() {
            Log.d(TAG, "getText: ");
            return null;
        }

        @Override
        public void onRetrieveFingerSearchContextResponse(String s, String s1, int i) {
            Log.d(TAG, "onRetrieveFingerSearchContextResponse: ");
        }
    }

对webView进行设置

mX5WebView2.getSettings().setJavaScriptEnabled(true);
IX5WebViewExtension x5WebViewExtension = mX5WebView2.getX5WebViewExtension();
if (x5WebViewExtension != null) {
    x5WebViewExtension.enterSelectionMode(true);
    x5WebViewExtension.setWebViewClientExtension(new ProxyWebViewClientExtensionX(mX5WebView2));
    x5WebViewExtension.setSelectListener(new ISelectionInterfaceX(mX5WebView2));
} else {
    Log.e(TAG, "x5还没有准备好 - x5WebViewExtension is null.");
}
mX5WebView2.loadDataWithBaseURL(null, mH1, "text/html", "UTF-8", null);

x5内核加载问题

手机上并不一定会有x5浏览器内核。 没有x5内核,不进行设置。长按菜单还是默认的。

App如何首次加载x5内核? App 在启动后(例如在 Application 的 onCreate 中)立刻调用 QbSdk 的预加载接口 initX5Environment ,可参考接入示例或API文档(https://x5.tencent.com/tbs/sdk.html). 需要注意的是: 当本地没有宿主x5内核可用,此时需要在wifi条件下下载x5内核(23M左右,耗时90秒左右),如果在此之前打开webview可能导致无内核可用而使用系统内核的情况