1. 程式人生 > >Android Zxing 相簿二維碼/條碼圖片識別

Android Zxing 相簿二維碼/條碼圖片識別

Android Zxing相簿二維碼/條碼圖片識別

在網上搜了好多例子放到專案中都會有些問題,有的是隻能識別二維碼圖片無法識別條碼圖片,有的是可以識別條碼但是識別率特別低。下面就介紹我整理過後在專案中使用的相簿圖片二維碼條碼識別整合步驟。

第一步 新增Zxing依賴

這裡只介紹Android Studio中新增依賴的方法。
在app資料夾中build.gradle中的dependencies程式碼塊兒中新增如下配置(ZXing版本根據需要引入):

 compile 'com.google.zxing:core:3.2.0'

第二步 跳轉相簿

在專案中要跳轉相簿的點選事件中呼叫次方法,呼叫此方法後會跳轉系統的相簿介面,在系統相簿介面選擇好圖片後會走activity的onActivityResult回撥方法,並帶回選中的圖片的Uri。

     publicvoid goToPhotoAlbum() {
        Intent intent = new Intent(Intent.ACTION_PICK, null);
        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        activity.startActivityForResult(intent, IntentConstant.REQUEST
_PHOTO_ALBUM); }

第三步 在Activity的onActivityResult中處理Intent

在相簿介面選中圖片後會走onActivityResult回撥方法。在回撥方法中獲取到intent中圖片的Uri,再通過Uri獲取到圖片的真實路徑,獲取到路徑後就可以識別圖片

   @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        // RESULT_OK 為Activity類中的常量,值為-1
if (resultCode == RESULT_OK && requestCode == 11) { String photoPath = getRealFilePath(mContext,data.getData()); if (null == photoPath){ // 路徑為null則獲取圖片失敗 Toast.makeText(this, "圖片獲取失敗,請重試", Toast.LENGTH_SHORT).show(); }else { // 解析圖片... parsePhoto(photoPath); } } }

通過Uri獲取圖片路徑的方法如下(覺得不好用就從別的地方找吧,能實現功能就行):

      @TargetApi(Build.VERSION_CODES.KITKAT)
    public static String getPath(Context context, Uri uri) {
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }
                // TODO handle non-primary volumes
            } else if (isDownloadsDocument(uri)) {
                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
                return getDataColumn(context, contentUri, null, null);
            } else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }
                final String selection = "_id=?";
                final String[] selectionArgs = new String[]{
                        split[1]
                };
                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        } else if ("content".equalsIgnoreCase(uri.getScheme())) {
            if (isGooglePhotosUri(uri))
                return uri.getLastPathSegment();
            return getDataColumn(context, uri, null, null);
        } else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
        return null;
    }

第四步 解析二維碼/條碼圖片

parsePhoto方法就是識別圖片二維碼條碼的方法,建議放到非同步任務中去可以使用RxJava或者AsyncTask下面分別介紹兩種實現方式:

RxJava實現方式:

需要在build.gradle中新增Rxjava和RxAndroid依賴:

   //RxJava
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    compile 'io.reactivex.rxjava2:rxjava:2.0.1'

非同步操作程式碼:

在parsePhoto方法中定義被觀察者:

    public void parsePhoto(final String path){
        Observable<String> observable = new Observable<String>() {
            @Override
            protected void subscribeActual(Observer<? super String> observer) {
                // 解析二維碼/條碼
                String result = QRCodeDecoder.syncDecodeQRCode(path);
                // 要做判空,不然可能會報空指標異常
                result = (null == result)?"":result;
                observer.onNext(result);
            }
        };
        // RxJava 根據圖片路徑獲取ZXing掃描結果的過程執行在io執行緒,獲取到結果後的操作在主執行緒
        observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);
    }

定義觀察者:

    private Observer<String> observer = new Observer<String>() {
        @Override
        public void onSubscribe(Disposable d) {}
        @Override
        public void onNext(String value) {
            if(StringUtils.isEmpty(value)){
                Toast.makeText(this, "未識別到二維碼/條碼", Toast.LENGTH_SHORT).show();
            }else {
                // 識別到二維碼/條碼內容:value
              }
        }
        @Override
        public void onError(Throwable e) {}
        @Override
        public void onComplete() {}
    };

AsyncTask實現方式:

    public void parsePhoto(final String path){
        AsyncTask myTask = new AsyncTask<String, Integer, String>() {
            @Override
            protected String doInBackground(String... params) {
                // 解析二維碼/條碼
                return QRCodeDecoder.syncDecodeQRCode(path);
            }
            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);
                if(null == s){
                    Toast.makeText(activity, "圖片獲取失敗,請重試", Toast.LENGTH_SHORT).show();
                }else {
                    // 識別出圖片二維碼/條碼,內容為s
                }
            }
        }.execute(path);
    }

解析二維碼的工具類QRCodeDecoder如下所示:

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.HybridBinarizer;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
/**
 * 描述:解析二維碼圖片
 */
public class QRCodeDecoder {
    public static final Map<DecodeHintType, Object> HINTS = new EnumMap<>(DecodeHintType.class);
    static {
        List<BarcodeFormat> allFormats = new ArrayList<>();
        allFormats.add(BarcodeFormat.AZTEC);
        allFormats.add(BarcodeFormat.CODABAR);
        allFormats.add(BarcodeFormat.CODE_39);
        allFormats.add(BarcodeFormat.CODE_93);
        allFormats.add(BarcodeFormat.CODE_128);
        allFormats.add(BarcodeFormat.DATA_MATRIX);
        allFormats.add(BarcodeFormat.EAN_8);
        allFormats.add(BarcodeFormat.EAN_13);
        allFormats.add(BarcodeFormat.ITF);
        allFormats.add(BarcodeFormat.MAXICODE);
        allFormats.add(BarcodeFormat.PDF_417);
        allFormats.add(BarcodeFormat.QR_CODE);
        allFormats.add(BarcodeFormat.RSS_14);
        allFormats.add(BarcodeFormat.RSS_EXPANDED);
        allFormats.add(BarcodeFormat.UPC_A);
        allFormats.add(BarcodeFormat.UPC_E);
        allFormats.add(BarcodeFormat.UPC_EAN_EXTENSION);
        HINTS.put(DecodeHintType.TRY_HARDER, BarcodeFormat.QR_CODE);
        HINTS.put(DecodeHintType.POSSIBLE_FORMATS, allFormats);
        HINTS.put(DecodeHintType.CHARACTER_SET, "utf-8");
    }
    private QRCodeDecoder() {
    }
    /**
     * 同步解析本地圖片二維碼。該方法是耗時操作,請在子執行緒中呼叫。
     *
     * @param picturePath 要解析的二維碼圖片本地路徑
     * @return 返回二維碼圖片裡的內容 或 null
     */
    public static String syncDecodeQRCode(String picturePath) {
        return syncDecodeQRCode(getDecodeAbleBitmap(picturePath));
    }
    /**
     * 同步解析bitmap二維碼。該方法是耗時操作,請在子執行緒中呼叫。
     *
     * @param bitmap 要解析的二維碼圖片
     * @return 返回二維碼圖片裡的內容 或 null
     */
    public static String syncDecodeQRCode(Bitmap bitmap) {
        Result result = null;
        RGBLuminanceSource source = null;
        try {
            int width = bitmap.getWidth();
            int height = bitmap.getHeight();
            int[] pixels = new int[width * height];
            bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
            source = new RGBLuminanceSource(width, height, pixels);
            result = new MultiFormatReader().decode(new BinaryBitmap(new HybridBinarizer(source)), HINTS);
            return result.getText();
        } catch (Exception e) {
            e.printStackTrace();
            if (source != null) {
                try {
                    result = new MultiFormatReader().decode(new BinaryBitmap(new GlobalHistogramBinarizer(source)), HINTS);
                    return result.getText();
                } catch (Throwable e2) {
                    e2.printStackTrace();
                }
            }
            return null;
        }
    }
    /**
     * 將本地圖片檔案轉換成可解碼二維碼的 Bitmap。為了避免圖片太大,這裡對圖片進行了壓縮。感謝 https://github.com/devilsen 提的 PR
     *
     * @param picturePath 本地圖片檔案路徑
     * @return
     */
    private static Bitmap getDecodeAbleBitmap(String picturePath) {
        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(picturePath, options);
            int sampleSize = options.outHeight / 400;
            if (sampleSize <= 0) {
                sampleSize = 1;
            }
            options.inSampleSize = sampleSize;
            options.inJustDecodeBounds = false;
            return BitmapFactory.decodeFile(picturePath, options);
        } catch (Exception e) {
            return null;
        }
    }
}

以上就是本人實現的相簿圖片二維碼/條碼識別的全部內容,如有錯誤歡迎指正!