Android入门教程 | WebView (二)

自定义长按文字弹出的菜单

长按 WebView 中的文字,会有选择光标,并且弹出一个小菜单。 一般菜单里会有「复制」,「搜索」等等选项。

这个菜单可以由开发者自定义。让弹出的菜单显示其他选项。 比如弹出「复制」「翻译」。

一般步骤:

  • 设计监听器
  • 自定义WebView,复写方法
  • 定义Js接口

需要新建2个 Java 文件,WebChooseActionListenerCustomClickWebView

定义监听器

监听器 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<String> 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<String> 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

<com.rustfisher.tutorial2020.web.longmenu.CustomClickWebView
    android:id="@+id/web1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

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 零基础入门教程视频参考

全部评论

相关推荐

02-22 20:28
重庆大学 Java
程序员牛肉:首先不要焦虑,你肯定是有希望的。 首先我觉得你得好好想一想自己想要什么。找不到开发岗就一定是失败的吗?那开发岗的35岁危机怎么说?因此无论是找工作还是考公我觉得你都需要慎重的想一想。但你一定要避开这样一个误区:“我是因为找不到工作所以不得不选择考公”。 千万不要这么想。你这个学历挺好的了,因此你投后端岗肯定是有面试机会的。有多少人简历写的再牛逼,直接连机筛简历都过不去有啥用?因此你先保持自信一点。 以你现在的水平的话,其实如果想要找到暑期实习就两个月:一个月做项目+深挖,并且不断的背八股。只要自己辛苦一点,五月份之前肯定是可以找到暑期实习的,你有点太过于高看大家之间的技术差距了。不要焦虑不要焦虑。 除此之外说回你这个简历内容的话,基本可以全丢了。如果想做后端,先踏踏实实做两个项目再说+背八股再说。如果想考公,那就直接备战考公。 但是但是就像我前面说的:你考公的理由可以是因为想追求稳定,想追求轻松。但唯独不能是因为觉得自己找不到工作。不能这么小瞧自己和自己的学历。
点赞 评论 收藏
分享
双飞二本嵌入式求拷打我是在&nbsp;BOSS&nbsp;上投递的简历,好多都没人回复,这是开场白和简历求大神帮忙看看。您好!我是2025届应届生,最快可在一周内上岗,能够实习六个月以上,并接受加班。以下是我的核心优势和相关经验:1.&nbsp;嵌入式开发能力:&nbsp;&nbsp;&nbsp;熟练掌握STM32系列单片机及其外设(如GPIO、定时器、ADC、DAC、I2C、SPI、UART等),能够独立完成硬件驱动开发和调试。&nbsp;&nbsp;熟悉FreeRTOS实时操作系统,具备多任务调度和资源管理经验。&nbsp;&nbsp;熟悉LVGL图形库开发,能够实现嵌入式设备的图形界面设计。2.&nbsp;硬件设计能力:&nbsp;&nbsp;&nbsp;具备PCB设计经验,曾为2023年工创赛物流搬运赛道设计小车主板,带领团队获得国家级银奖。&nbsp;&nbsp;&nbsp;熟悉硬件原理图分析,能够快速理解并调试硬件电路。3.&nbsp;机器人开发与竞赛经验:&nbsp;&nbsp;&nbsp;在全国大学生智能车竞赛、ROS机器人竞赛中多次获得国家级奖项,具备丰富的机器人开发经验。&nbsp;&nbsp;&nbsp;熟悉Linux环境,对ROS和ROS&nbsp;2有一定了解,能够进行机器人系统的开发与调试。4.&nbsp;编程能力:&nbsp;&nbsp;&nbsp;熟悉C/C++,熟悉Python,能够高效完成嵌入式开发和算法实现。&nbsp;&nbsp;&nbsp;具备良好的代码规范和文档编写能力。5.&nbsp;团队协作与领导能力:&nbsp;&nbsp;&nbsp;在多个项目中担任核心开发或团队负责人,具备良好的沟通能力和团队协作精神。&nbsp;&nbsp;&nbsp;在工创赛中带领团队完成项目规划、任务分配和技术攻关,展现了较强的领导力。我对嵌入式开发、机器人技术和智能硬件充满热情,期待加入贵公司,与团队共同成长,为公司创造价值!如果有合适的岗位,欢迎随时联系我,期待进一步沟通!
沉淀一会:嵌入式就是狗屎
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务