通過JavaScript實現在Android WebView中點擊查看圖片,長按識別二維碼

序言

最近的項目中,客戶需要在WebView中實現長按識別二維碼的功能。但是原有的圖片已經有點擊查看圖片功能。要不破壞原有的功能,還能添加長按事件。這是第一次遇到這種需求。最後我還是完成了這個功能。但是在完成的過程中也遇到一些坑。在此記錄一下,先看一下我實現的效果。

1.原有的點擊查看圖片功能
在這裏插入圖片描述

2.長按識別二維碼
在這裏插入圖片描述

3識別失敗給出提示
在這裏插入圖片描述

實現

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輸入進去檢查有沒有語法錯誤。
在這裏插入圖片描述

如果出現語法錯誤可以快速定位
在這裏插入圖片描述

當我們在Android中調試WebView中的JavaScript代碼時,也可以通過chrome來遠程調試。首先設置webview

webView.setWebContentsDebuggingEnabled(true);

然後再chrome瀏覽器的地址欄中輸入

chrome://inspect/#devices

然後點擊inspect

在這裏插入圖片描述

理論效果是這樣的

在這裏插入圖片描述

但是實際上,部分手機加載不出來這個界面。我的手機就加載不出來。所以最好的方式還是使用

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

在Android Studio 的LogCat中過濾chromium 就可以看到log。可以在console.log中打印變量值。如果沒有輸出的話。回到第一步,將代碼拷貝到chrom瀏覽器中檢查是否有語法錯誤。

在這裏插入圖片描述

4.WebView不執行JavaScript代碼。最開始我在onPageFinished中。通過下面的代碼執行js。

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

但是效果是。js始終未執行。後來查閱資料發現這種方式執行js。有最大長度的限制。後面使用

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

該方法是Android4.4以後添加,是異步執行。
在這裏插入圖片描述

具體的代碼如下

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章