美文网首页Android开发经验谈Android开发Android技术知识
通过JavaScript实现在Android WebView中点

通过JavaScript实现在Android WebView中点

作者: zhuguohui | 来源:发表于2018-10-17 16:59 被阅读29次

    序言

    最近的项目中,客户需要在WebView中实现长按识别二维码的功能。但是原有的图片已经有点击查看图片功能。要不破坏原有的功能,还能添加长按事件。这是第一次遇到这种需求。最后我还是完成了这个功能。但是在完成的过程中也遇到一些坑。在此记录一下,先看一下我实现的效果。

    1.原有的点击查看图片功能

    1.gif

    2.长按识别二维码

    2.gif

    3识别失败给出提示

    3.gif

    实现

    1.长按事件的监测

    背景知识

    要实现对长按事件的检测主要通过HTML5 中的touchstart ,touchmove,touchend 事件。来实现。一开始触摸事件touchstart、touchmove和touchend是iOS版Safari浏览器为了向开发人员传达一些信息新添加的事件。因为ios设备既没有鼠标也没有键盘,所以在为移动Safari浏览器开发交互性网页的时候,PC端的鼠标和键盘事件是不够用的。

    在iPhone 3Gs发布的时候,其自带的移动Safari浏览器就提供了一些与触摸(touch)操作相关的新事件。随后,Android上的浏览器也实现了相同的事件。触摸事件(touch)会在用户手指放在屏幕上面的时候、在屏幕上滑动的时候或者是从屏幕上移开的时候出发。下面具体说明:

    touchstart事件:当手指触摸屏幕时候触发,即使已经有一个手指放在屏幕上也会触发。
    touchmove事件:当手指在屏幕上滑动的时候连续地触发。在这个事件发生期间,调用preventDefault()事件可以阻止滚动。
    touchend事件:当手指从屏幕上离开的时候触发。
    touchcancel事件:当系统停止跟踪触摸的时候触发。关于这个事件的确切出发时间,文档中并没有具体说明,咱们只能去猜测了。

    具体的思路

    1.在touchstart 事件中通过setTimeout 方法延迟500毫秒触发长按事件,并保存返回的事件id
    2.在touchmove时将事件id重置为0,并取消长按事件。(防止在手机滑动图片的时候误触发事件)
    3.在touchend的时候判断是事件id是否为0,(在长按事件触发时和手机滑动时 事件id会置为0),如果不为0.则表示长按事件还没有发生,也不是误差。表示500毫秒内手指抬起了,触发单击事件。

    具体的代码如下

    var objs = document.getElementsByTagName("img");
    var timeOutEvent=0;
    var imageUrl="";
    for(var i=0; i<objs.length; i++) {
        var img=objs[i];
        mobile.addImageUrl(img.src);//收集网页中图片的url
         //清除之前设置的,防止重复设置
         img.removeEventListener('touchstart',touchstart)
         img.removeEventListener('touchmove',touchmove)
         img.removeEventListener('touchend',touchend)
    
         //注册事件
        img.addEventListener('touchstart',touchstart)
        img.addEventListener('touchmove',touchmove)
        img.addEventListener('touchend',touchend)
    }
    function touchstart(e){
          timeOutEvent = setTimeout("longPress()",500);
         imageUrl=this.src;
    }
    
    function touchmove(e){
        clearTimeout(timeOutEvent);
         timeOutEvent = 0;
    }
    
    function touchend(e){
       clearTimeout(timeOutEvent);
              if(timeOutEvent!=0){
                   //展示图片
                   mobile.showImage(imageUrl);
               }
                return false;
    }
    
    function longPress(){
        timeOutEvent = 0;
       if(typeof mobile!="undefined"){
            mobile.scanCode(imageUrl);
        }
    }
    
    

    二维码识别

    思路如下

    1.向webview注入js对象mobile。再通过js在长按时,向mobile的scanCode方法传入图片的url
    2.拿到url以后使用Glide 下载相应的图片,获得bitmap
    3.通过zxing实现解码,回调ScanListener的onScanSuccess()方法,返回结果。

    问题总结

    1.Glide的into方法必须在主线程中启动。而JavaScript调用本地代码的线程不是主线程。在其他线程调用into方法时会出现既加载不出图片,也不会报错的现象。

    2.二维码扫描使用的是android-zxingLibrary库。比较方便
    地址是android-zxingLibrary

    3.关于调试。由于涉及到JavaScript和本地代码的联调。而最困难的是JavaScript的调试。建议大家在每次修改完JavaScript代码以后使用chrome的开发者模式,将JavaScript代码输入通过console输入进去检查有没有语法错误。


    a.png

    如果出现语法错误可以快速定位


    b.png

    当我们在Android中调试WebView中的JavaScript代码时,也可以通过chrome来远程调试。首先设置webview

    webView.setWebContentsDebuggingEnabled(true);
    

    然后再chrome浏览器的地址栏中输入

    chrome://inspect/#devices
    

    然后点击inspect

    c.png

    理论效果是这样的

    d.png

    但是实际上,部分手机加载不出来这个界面。我的手机就加载不出来。所以最好的方式还是使用

    console.log("this is a log");
    

    在Android Studio 的LogCat中过滤chromium 就可以看到log。可以在console.log中打印变量值。如果没有输出的话。回到第一步,将代码拷贝到chrom浏览器中检查是否有语法错误。

    e.png

    4.WebView不执行JavaScript代码。最开始我在onPageFinished中。通过下面的代码执行js。

    webview.loadUrl("javascript:"+initJS);
    

    但是效果是。js始终未执行。后来查阅资料发现这种方式执行js。有最大长度的限制。后面使用

      view.evaluateJavascript("javascript:" + initJs, null);
    

    该方法是Android4.4以后添加,是异步执行。

    f.png

    具体的代码如下

    package com.zgh.scandodedemo.util;
    
    import android.content.Context;
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.graphics.drawable.Drawable;
    import android.os.Handler;
    import android.os.Looper;
    import android.text.TextUtils;
    import android.util.Log;
    import android.webkit.JavascriptInterface;
    import android.widget.Toast;
    
    import com.bumptech.glide.Glide;
    import com.bumptech.glide.request.animation.GlideAnimation;
    import com.bumptech.glide.request.target.SimpleTarget;
    import com.uuzuche.lib_zxing.activity.CodeUtils.AnalyzeCallback;
    import com.zgh.scandodedemo.activity.ImageBrowserActivity;
    import com.zgh.scandodedemo.util.CodeUtils;
    
    import java.util.ArrayList;
    
    
    /**
     * Created by zhuguohui on 2018/10/9.
     */
    
    public class Mobile {
        Context context;
        Handler handler = new Handler(Looper.getMainLooper());
        ArrayList<String> imageURLList = new ArrayList<>();
    
        public Mobile(Context context) {
            this.context = context;
        }
    
        @JavascriptInterface
        public void scanCode(final String imageUrl) {
            if (scanListener != null) {
                scanListener.onScanStart();
            }
            if (TextUtils.isEmpty(imageUrl)) {
                if (scanListener != null) {
                    scanListener.onScanFailed("图片地址为空");
                }
            } else {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        getImage(imageUrl);
                    }
                });
    
    
            }
        }
    
        @JavascriptInterface
        public void addImageUrl(String url) {
            imageURLList.add(url);
        }
    
        @JavascriptInterface
        public void showImage(String url) {
            Intent intent = new Intent(context, ImageBrowserActivity.class);
            intent.putStringArrayListExtra(ImageBrowserActivity.IMAGE_BROWSER_LIST, imageURLList);
            intent.putExtra(ImageBrowserActivity.IMAGE_BROWSER_INIT_SRC, url);
            context.startActivity(intent);
        }
    
        private void getImage(String imageUrl) {
            Glide.with(context.getApplicationContext()).load(imageUrl)
                    .asBitmap()
                    .into(new SimpleTarget<Bitmap>() {
                        @Override
                        public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
    
                            CodeUtils.analyzeBitmap(resource, new AnalyzeCallback() {
                                @Override
                                public void onAnalyzeSuccess(Bitmap mBitmap, String result) {
                                    if (scanListener != null) {
                                        scanListener.onScanSuccess(result);
                                    }
                                }
    
                                @Override
                                public void onAnalyzeFailed() {
                                    if (scanListener != null) {
                                        scanListener.onScanFailed("未识别到二维码");
                                    }
                                }
                            });
    
                        }
    
                        @Override
                        public void onLoadFailed(Exception e, Drawable errorDrawable) {
                            super.onLoadFailed(e, errorDrawable);
                            if (scanListener != null) {
                                scanListener.onScanFailed("加载图片失败[" + e.getMessage() + "]");
                            }
                        }
                    });
        }
    
        public interface ScanListener {
            void onScanStart();
    
            void onScanFailed(String info);
    
            void onScanSuccess(String result);
        }
    
        private ScanListener scanListener;
    
        public void setScanListener(ScanListener scanListener) {
            this.scanListener = scanListener;
        }
    }
    
    

    源码下载

    ScanDemo

    相关文章

      网友评论

        本文标题:通过JavaScript实现在Android WebView中点

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