美文网首页Android开发学习AndroidAndroid
WebView自定义长按选择,实现收藏/分享选中文本。

WebView自定义长按选择,实现收藏/分享选中文本。

作者: 恋猫月亮 | 来源:发表于2017-06-19 07:51 被阅读3207次

    Hello,大家吼,吾就是那个挖坑不止的郭小喵,不管有没有想我(˶‾᷄ ⁻̫ ‾᷅˵)的,这次就分享快速实现一个自定义WebView的小控件吧,效果如下图,废话不多说,我们直接开撸吧。**

    (PS : ╮(╯▽╰)╭如果你翻一翻我过去的文章,你会发现90%的开场白,前几个问候语都是一样的。)

    效果图(1.3M)

    一、前言

    **** 戳这里可以去DEMO,来吧 ** **

    相信刚接触android不久的同志们,在面对产品提出的 :

    “自定义WebView页面中,长按文本的弹出选项、点击选择后,分享、转发、收藏选择文本” 
    

    这样的需求时,第一反应大部分是:这是系统行为,如果实现需要在web端实现。

    但是web端实现的局限性太大,曾经也有过监听系统粘贴板,在用户点击复制的时候实现其他的逻辑,但是这样用户体验不好,所以自定义WebView中长按的弹出菜单,并在点击时返回选中文本的小控件闪亮登场┏ (ω)=。

    二、自定义长按弹出菜单

    这一步实现其实很简单,首先创建一个CustomActionWebView继承系统WebView,然后重写下面两个方法。

    这两个方法会在用户长按选择web文本时,在弹出菜单前被调用。它们之间的区别在于,第一个方法的菜单弹出方式,指定了默认的type。我们并不关心弹出的item类型是什么,我们只需要拦截下来ActionMode,然后返回我们自己的自定义ActionMode即可。

    @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);
    }
    

    这里我们所做的事是:

    • 1、把原本的actionMode对象保存到mActionMode中。
    • 2、清空原本actionMode中的MenuItem。
    • 3、添加我们自定义的item到actionMode中。
    • 4、重定义每个menuItem的点击事件。
    • 5、在点击事件中通过执行js,获取选中文本。
    • 6、通过上面保存的** mActionMode**,释放弹出菜单(不释放会内存泄漏)。
    • 7、返回新填充的actionMode给系统。
    /**
     * 处理item,处理点击
     * @param actionMode
     */
    private ActionMode resolveActionMode(ActionMode actionMode) {
        if (actionMode != null) {
            final Menu menu = actionMode.getMenu();
            mActionMode = actionMode;
            menu.clear();
            for (int i = 0; i < mActionList.size(); i++) {
                menu.add(mActionList.get(i));
            }
            for (int i = 0; i < menu.size(); i++) {
                MenuItem menuItem = menu.getItem(i);
                menuItem.setOnMenuItemClickListener(new Item.OnMenuItemClickListener() {
                    @Override
                    public boolean onMenuItemClick(MenuItem item) {
                        getSelectedData((String) item.getTitle());
                        releaseAction();
                        return true;
                    }
                });
            }
        }
        mActionMode = actionMode;
        return actionMode;
    }
    
    实现效果

    三、获取选中文本

    光自定义菜单,拿不到选中文本也没意义,那么如何获取选中文本呢?这里如果不转个弯,还真会在南墙在撞死( ̄^ ̄)ゞ,所以,我们伟大的js就出现了。

    首先,我们自定义一个接口,用于监听js方法,其中@JavascriptInterface是关键的所在,** 在callback中获取js端返回的数据**。

    然后将这个接口,在CustomActionWebView中add进去(一般是在初始化和页面加载完成时都add一次),并指定js端调用的接口名称为“ JSInterface”。(ps:别忘了开始webview的js允许哦。)

    public void linkJSInterface() {
       addJavascriptInterface(new ActionSelectInterface(this), "JSInterface");
    }
    
    ···
    
    /**
     * js选中的回调接口
     */
    private class ActionSelectInterface {
    
        CustomActionWebView mContext;
    
        ActionSelectInterface(CustomActionWebView c) {
            mContext = c;
        }
    
        @JavascriptInterface
        public void callback(final String value, final String title) {
            if(mActionSelectListener != null) {
                mActionSelectListener.onClick(title, value);
            }
        }
    }
    

    最后在点击时,通过执行js来获取web中选中的文本。在上面自定义菜单中第5项,点击menu时,执行下方js代码,便可以把选中的item和文本,回调到上面的接口中的callback。

    熟悉js的小伙伴已经看出来吧:

    • 其实就是定义了一个js的function,然后在webview中执行这个方法。
    • 定义的这个名为function getSelectedText()的js方法,有两个变量:txt和title。
    • title是从原生中传入的item名字,txt是通过window去获取web中选中的文本。
    • 最后回到上面我们注册的js方法名JSInterface,通过它的callback方法,将文本和name返回到原生代码callback中。
    • 根据版本不同,执行js方法的接口也不一样。
    /**
     * 点击的时候,获取网页中选择的文本,回掉到原生中的js接口
     * @param title 传入点击的item文本,一起通过js返回给原生接口
     */
    private void getSelectedData(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;" +
                "}" +
                "JSInterface.callback(txt,title);" +
                "})()";
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            evaluateJavascript("javascript:" + js, null);
        } else {
            loadUrl("javascript:" + js);
        }
    }
    

    四、最后

    既然自定义item实现了,点击和选择文本返回也实现了,在callback中,你就可以愉悦的收藏,或者分享你所选中的文本啦(◐‿◑),操作一气呵成,有没有被惊艳到呢?

    如果感兴趣的,可以下载demo看下,同时CustomActionWebView也封装好了远程依赖,欢迎使用。

    https://github.com/CarGuo/CustomActionWebView

    写文章也是个累活

    相关文章

      网友评论

      • 三脚猫44_5b53:您好,想请教一下,用 Cordova 生成的 android app,有办法实现文中的功能吗?谢谢指点。
      • 慕偶:魅族手机 flyme系统6.0.1还是只能弹出系统的复制搜索菜单 有办法解决么??谢谢
      • fly大梦想家:ios该怎么搞啊
        恋猫月亮:@fly大梦想家 😂😂😂这问题,我真不知道怎么回答
      • feel_5172:在4.4.2 版本上 没有反应
      • baa0ac740b1f:卧槽!有用
      • cd669ffe9f5f:厉害了,完全满足我的需求,先试一波~
      • c990e9d19f46:和h5那边怎么交互呀,h5那边需要做什么。
        恋猫月亮: @c990e9d19f46 本文不需要和h5交互哦,不过github的demo有和h5交互的例子,你可以看看
      • boboyuwu: if (actionMode != null) {
        final Menu menu = actionMode.getMenu();
        mActionMode = actionMode;第一处mActionMode = actionMode

        mActionMode = actionMode;
        return actionMode; 第二处mActionMode = actionMode;


        boboyuwu:@恋猫月亮 那么赋值二次完全看不出来什么意义啊赋值的都是resolveActionMode传过来的
        恋猫月亮: @boboyuwu 这个只是有时候会null
      • boboyuwu:很棒很赞,之前公司有这种需求,但是找遍所有网站居然搜不到任何资料,根本不知道长按会调用那个方法。。。当时用js监听长按底部显示一个View做的 有二个问题请教下哈
        1. mActionMode = actionMode;这个为什么会调用二次呢?
        2.你是从哪里知道长按会调用这个菜单呢
      • boboyuwu:很棒很赞,之前公司有这种需求,但是找遍所有网站居然搜不到任何资料,根本不知道长按会调用那个方法。。。当时用js监听长按底部显示一个View做的 有二个问题请教下哈
        1. mActionMode = actionMode;这个为什么会调用二次呢?
        2.你是从哪里知道长按会调用这个菜单呢
        恋猫月亮: @boboyuwu 看源码调试的,至于调用两遍是??
      • thsai:关于这个自定义长按弹出的问题:
        1.华为、锤子手机6.0以下系统是弹不出自定义的
        2.系统6.0以下弹出在顶部,而不是悬浮的
        3. 乐视手机点击自定义的item没反应
        楼主如果有好的解决方案希望通知我 谢谢
        恋猫月亮: @thsai 弹出来了但是没回调??我去……没有乐视,方便在源码里看看是不是被拦截了吗?
        thsai:不 它压根就没有执行onclick事件
        恋猫月亮: @thsai 系统不支持目前就没什么办法了,除非有自定义web内核的,6.0以下的话确实是顶部,看能不注入js监听选中的位置。可是没反应是js执行无效吗?
      • 后来Memory:不懂JS,看到后面JS那块就不明白了
        恋猫月亮: @后来Memory js其实挺好上手的,其实就是写了一个js方法,然后让webview执行这分方法。方法里获取了网页选中的文本?
      • 1e2578a410bd:我也是通信工程毕业的,哥儿们
        恋猫月亮: @41288159 👊👊👊
      • f726002671d9:貌似今天增加了一些关于web东西?
        恋猫月亮: @潜水的鱼不想浮起来 是的,一些关于web的api,回调,和js通信相关的
      • c37844735d3d:我记得你
        恋猫月亮: @Setsunal 😉谢谢

      本文标题:WebView自定义长按选择,实现收藏/分享选中文本。

      本文链接:https://www.haomeiwen.com/subject/omjbqxtx.html