Android入门教程 | WebView (二)

自定义长按文字弹出的菜单
长按 WebView 中的文字,会有选择光标,并且弹出一个小菜单。 一般菜单里会有「复制」,「搜索」等等选项。
【Android入门教程 | WebView (二)】这个菜单可以由开发者自定义。让弹出的菜单显示其他选项。 比如弹出「复制」「翻译」。
一般步骤:

  • 设计监听器
  • 自定义WebView,复写方法
  • 定义Js接口
需要新建2个 Java 文件,WebChooseActionListener,CustomClickWebView
定义监听器 监听器 WebChooseActionListener,用来传输用户的选择。
public interface WebChooseActionListener {/** * @param itemTitle 选项的标题 * @param txt选中的文字 */ void onChosen(String itemTitle, String txt); }

txt是用户在 WebView上选中的文字。如何获取到选中的文字呢? 本例中用 js 方法来获取。
自定义 WebView js 和 Java 的交互
定义 CustomJsInterface (本例中把它放在了 CustomClickWebView 里),其中给 chooseAction 添加注解 @JavascriptInterface,将其定义为沟通的桥梁。 js中会调用这个 chooseAction 方法。
为了获取用户在 WebView 上选中的文字,需要执行 js 代码,参考exeSelectTextJs 方法。 js代码中的RFJSInterface,和Java中addJavascriptInterface传入的名字必须一致。
addJavascriptInterface(new CustomJsInterface(this), "RFJSInterface");

js代码中"RFJSInterface.chooseAction(txt,title); "就是在调用Java中定义的方法。
控制菜单
想要控制 WebView 中弹出的菜单,需要复写 startActionMode 方法,把默认的菜单替换成自定义的菜单。调用 Menu.add() 方法添加选项。
自定义的菜单选项,从外面传进来。
@Override public ActionMode startActionMode(ActionMode.Callback callback) { ActionMode actionMode = super.startActionMode(callback); return resolveActionMode(actionMode); }@Override public ActionMode startActionMode(ActionMode.Callback callback, int type) { ActionMode actionMode = super.startActionMode(callback, type); return resolveActionMode(actionMode); }private ActionMode resolveActionMode(ActionMode actionMode) { if (actionMode != null) { final Menu menu = actionMode.getMenu(); mCurActionMode = actionMode; menu.clear(); for (int i = 0; i < mMenuItemNameList.size(); i++) { menu.add(mMenuItemNameList.get(i)); } for (int i = 0; i < menu.size(); i++) { MenuItem menuItem = menu.getItem(i); menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { exeSelectTextJs((String) item.getTitle()); releaseAction(); return true; } }); } } mCurActionMode = actionMode; return actionMode; }

CustomClickWebView 代码:
import android.content.Context; import android.util.AttributeSet; import android.view.ActionMode; import android.view.Menu; import android.view.MenuItem; import android.webkit.JavascriptInterface; import android.webkit.WebView; import java.util.ArrayList; import java.util.List; public class CustomClickWebView extends WebView { private ActionMode mCurActionMode; private List mMenuItemNameList = new ArrayList<>(); private WebChooseActionListener mWebChooseActionListener; public CustomClickWebView(Context context) { super(context); }public CustomClickWebView(Context context, AttributeSet attrs) { super(context, attrs); }public CustomClickWebView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }@Override public ActionMode startActionMode(ActionMode.Callback callback) { ActionMode actionMode = super.startActionMode(callback); return resolveActionMode(actionMode); }@Override public ActionMode startActionMode(ActionMode.Callback callback, int type) { ActionMode actionMode = super.startActionMode(callback, type); return resolveActionMode(actionMode); }private ActionMode resolveActionMode(ActionMode actionMode) { if (actionMode != null) { final Menu menu = actionMode.getMenu(); mCurActionMode = actionMode; menu.clear(); for (int i = 0; i < mMenuItemNameList.size(); i++) { menu.add(mMenuItemNameList.get(i)); } for (int i = 0; i < menu.size(); i++) { MenuItem menuItem = menu.getItem(i); menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { exeSelectTextJs((String) item.getTitle()); releaseAction(); return true; } }); } } mCurActionMode = actionMode; return actionMode; }private void releaseAction() { if (mCurActionMode != null) { mCurActionMode.finish(); mCurActionMode = null; } }private void exeSelectTextJs(String title) { String js = "(function getSelectedText() {" + "var txt; " + "var title = \"" + title + "\"; " + "if (window.getSelection) {" + "txt = window.getSelection().toString(); " + "} else if (window.document.getSelection) {" + "txt = window.document.getSelection().toString(); " + "} else if (window.document.selection) {" + "txt = window.document.selection.createRange().text; " + "}" + "RFJSInterface.chooseAction(txt,title); " + "})()"; evaluateJavascript("javascript:" + js, null); }/** * 在外部调用 */ public void linkJSInterface() { addJavascriptInterface(new CustomJsInterface(this), "RFJSInterface"); }/** * @param actionList 弹出菜单 */ public void setActionList(List actionList) { mMenuItemNameList = actionList; }public void setActionSelectListener(WebChooseActionListener webChooseActionListener) { this.mWebChooseActionListener = webChooseActionListener; }/** * 隐藏消失Action */ public void dismissAction() { releaseAction(); }/** * js选中的回掉接口 */ private class CustomJsInterface {CustomClickWebView webView; CustomJsInterface(CustomClickWebView c) { webView = c; }/** * @param value 选中的文字 * @param title 菜单标题 */ @JavascriptInterface public void chooseAction(final String value, final String title) { if (mWebChooseActionListener != null) { mWebChooseActionListener.onChosen(title, value); } } } }

使用 layout 中使用这个 CustomClickWebView

Activity 中进行初始化配置
mWv1.getSettings().setJavaScriptEnabled(true); // 允许使用js mWv1.setActionList(Arrays.asList("复制", "翻译")); // 自定义菜单选项 mWv1.getSettings().setBlockNetworkImage(false); mWv1.linkJSInterface(); // 把js接口添加进去// 设置监听器,获取点击到的菜单和选中的文字 mWv1.setActionSelectListener(new WebChooseActionListener() { @Override public void onChosen(String title, String selectText) { Toast.makeText(getApplicationContext(), "(" + title + ") -> " + selectText, Toast.LENGTH_SHORT).show(); } });

(重写 startActionMode 的做法在 x5 WebView 上并不起作用。)
【Android 零基础入门教程视频参考】

    推荐阅读