教程:Android使用zxing+cameraview實現(xiàn)自定義二維碼掃描

很多圖文教程和視頻教程都喜歡啰嗦一大堆,才總結(jié)性地說一句:廢話不多說劲妙,下面開始湃鹊。我跟那些妖艷賤貨肯定是要不一樣的。開擼镣奋。

1. 集成zxing

打開項目build.gradle币呵,加入以下代碼:

// zxing 掃描生成二維碼和條形碼
compile 'com.google.zxing:core:3.3.0'

我們這里之所以只集成了zxing的核心包,核心包里面是解析/生成 二維碼/條形碼等一系列算法,并且余赢,包足夠小芯义。

2. 集成CameraView

點擊Google出品的CameraView到項目主頁,直接下載或者clone到本地妻柒,然后把里面的Library文件夾下面的內(nèi)容拷貝到自己的項目工程扛拨。

項目配置完畢。

3. 工作思路和流程

思路很簡單:拍照得到照片举塔,然后把照片交給zxing去解析绑警。我們只需要解析結(jié)果。

先上解析代碼:

    /**
     * 掃描圖片
     *
     * @param bitmap 圖片
     * @return zxing掃描初始結(jié)果
     */
    private Result scanningImage(Bitmap bitmap) {
        Map<DecodeHintType, String> hints1 = new Hashtable<>();
        hints1.put(DecodeHintType.CHARACTER_SET, "utf-8");
        RGBLuminanceSource source = new RGBLuminanceSource(bitmap);
        BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
        QRCodeReader reader = new QRCodeReader();
        Result result;
        try {
            result = reader.decode(bitmap1, hints1);
            scanResult(result.getText());
            return result;
        } catch (NotFoundException | ChecksumException | FormatException e) {
            // 解析照片上面的二維碼失敗后央渣,拍照重新解析
            if (mCameraView != null && mCameraView.isCameraOpened()) {
                mCameraView.takePicture();
            }
            e.printStackTrace();
        }
        return null;
    }

不過這里就遇到難題了计盒,因為CameraView默認拍攝的是攝像頭所支持的,當(dāng)前寬高比最高的照片芽丹。以目前我正在使用的魅族Pr5為例北启,2100像素拍出來的照片,隨隨便便都是8M以上拔第。這么大的照片暖庄,即使不會OOM,解析速度也會慢的令人發(fā)指楼肪。

事實上,在我的手機(魅族Pro5)上面測試惹悄,OOM倒是不會春叫,但結(jié)果是虛擬機開始頻繁GC,然后應(yīng)用卡死泣港。(其實這就是OOM)

所以暂殖,這個時候就要想到去壓縮圖片,使用到BitmapFactory的一系列方法当纱,對其進行壓縮呛每。因為CameraView得到的照片是一個byte數(shù)組,我們直接對這個數(shù)組進行生成Bitmap坡氯,然后再根據(jù)其寬高(因為每臺手機攝像頭都不一樣晨横,拍出來的照片也是千差萬別)計算壓縮比,進行壓縮箫柳,最后再將壓縮后的圖片傳給zxing進行解析手形。

流程圖:

graph TD
    AA[開始] --> A[拍照]
    A --> B[壓縮圖片]
    B --> C[發(fā)送圖片給zxing]
    C --> D[解析圖片上的二維碼]
    D --> F{Success or failed}
    F --> |Failed| A
    F --> |Success| G[Finish]
    

(圖有點抽象,將就著看)

在這個流程之中悯恍,比較耗時的地方一般是解析圖片库糠。zxing解析圖片的過程非常的慢,使用我的Pro5進行解析,壓縮400ms以內(nèi)夕春,解析700ms以內(nèi)婉商。使用比較低端的測試機站蝠,壓縮用了500ms(這方面耗時并不多苗沧,算法使用的Google的)繁扎,但是解析卻達到了4000ms——5000ms之間伪节。

目前懷疑這個耗時是因為使用了一個將Bitmap轉(zhuǎn)化為zxing所需的Bitmap的問題吼砂。zxing所需的Bitmap和我們平常使用的Bitmap格式不同顷帖,這一塊也沒有去看和仔細優(yōu)化美旧,應(yīng)該還可以提速一部分。

目前項目只能說可以使用贬墩。比較要注意的是讓相機開始拍照的時機問題榴嗅,因為掃描二維碼是打開相機后自動掃描,不可能讓用戶點擊拍照按鈕再去掃描陶舞。我采用的方式是打開相機頁面之后嗽测,在頁面上一個元素中加上postDelay,1000ms后開始獲取照片肿孵。不過這個也需要加一個判斷:CameraView.isCameraOpen()否則會出現(xiàn)打開頁面立馬退出唠粥,倒計時完成后依然會視圖去拍照,但是CameraView此時已經(jīng)被銷毀停做,強行使用被銷毀的對象晤愧,自然就會導(dǎo)致應(yīng)用崩潰了。

整個工程完全可以基于CameraView項目中原有的代碼開始使用蛉腌,只需要把代碼拷貝過去就行了官份。我這邊也只是集成在了項目之中,目前自己測試沒什么問題烙丛,但要是真在實際環(huán)境中使用的話舅巷,還需要大量的測試。等基本完善之后河咽,才會考慮將代碼開源出來钠右。

其實也并沒有什么開源的地方,代碼基本上都是各種copy過來的忘蟹,只是其中有很多地方需要一點一點去看飒房,去做優(yōu)化。

還有寒瓦,自定義掃描二維碼界面我就不說了情屹,繪制一個View的事情,不在本教程范圍內(nèi)杂腰。

目前基本就這些垃你,謝謝大家。

下面是代碼部分:

壓縮圖片

/**
     * 以字節(jié)的格式壓縮位圖
     *
     * @param bytes 拍照所得字節(jié)位圖
     * @return 壓縮后的Bitmap
     */
    private Bitmap byteForCompressBitmap(byte[] bytes) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
        options.inSampleSize = computeSampleSize(options, -1, 1920 * 1080);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
    }


    /**
     * 計算圖片樣本大小 for google
     *
     * @param options        BitmapOption 記得inJustBounds設(shè)置為true
     * @param minSideLength  最小邊長 一般為 -1
     * @param maxNumOfPixels 最大像素數(shù)
     * @return 返回可縮放倍數(shù)
     */
    public int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
        int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
        int roundedSize;
        if (initialSize <= 8) {
            roundedSize = 1;
            while (roundedSize < initialSize) {
                roundedSize <<= 1;
            }
        } else {
            roundedSize = (initialSize + 7) / 8 * 8;
        }
        return roundedSize;
    }

    /**
     * 計算圖片初始樣本大小
     *
     * @param options        開關(guān)
     * @param minSideLength  最小邊長
     * @param maxNumOfPixels 最大像素數(shù)目
     * @return 傳入圖片初始樣本大小
     */
    private int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
        double w = options.outWidth;
        double h = options.outHeight;
        int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
        int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));
        if (upperBound < lowerBound) {
            return lowerBound;
        }
        if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
            return 1;
        } else if (minSideLength == -1) {
            return lowerBound;
        } else {
            return upperBound;
        }
    }

上方zxing解析圖片中的二維碼缺失的RGBLuminanceSource類,如果直接使用zxing包中的RGBLuminanceSource類惜颇,因為接收的圖片格式不同皆刺,會導(dǎo)致不能運行。

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import com.google.zxing.LuminanceSource;

import java.io.FileNotFoundException;

/**
 * This class is used to help decode images from files which arrive as RGB data
 * from Android bitmaps. It does not support cropping or rotation.
 *
 * @author dswitkin@google.com (Daniel Switkin)
 */
public final class RGBLuminanceSource extends LuminanceSource {
    private final byte[] luminances;

    public RGBLuminanceSource(String path) throws FileNotFoundException {
        this(loadBitmap(path));
    }

    public RGBLuminanceSource(Bitmap bitmap) {
        super(bitmap.getWidth(), bitmap.getHeight());
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int[] pixels = new int[width * height];
        bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
        // In order to measure pure decoding speed, we convert the entire image
        // to a greyscale array
        // up front, which is the same as the Y channel of the
        // YUVLuminanceSource in the real app.
        luminances = new byte[width * height];
        for (int y = 0; y < height; y++) {
            int offset = y * width;
            for (int x = 0; x < width; x++) {
                int pixel = pixels[offset + x];
                int r = (pixel >> 16) & 0xff;
                int g = (pixel >> 8) & 0xff;
                int b = pixel & 0xff;
                if (r == g && g == b) {
                    // Image is already greyscale, so pick any channel.
                    luminances[offset + x] = (byte) r;
                } else {
                    // Calculate luminance cheaply, favoring green.
                    luminances[offset + x] = (byte) ((r + g + g + b) >> 2);
                }
            }
        }
    }

    private static Bitmap loadBitmap(String path) throws FileNotFoundException {
        Bitmap bitmap = BitmapFactory.decodeFile(path);
        if (bitmap == null) {
            throw new FileNotFoundException("Couldn't open " + path);
        }
        return bitmap;
    }

    @Override
    public byte[] getRow(int y, byte[] row) {
        if (y < 0 || y >= getHeight()) {
            throw new IllegalArgumentException(
                    "Requested row is outside the image: " + y);
        }
        int width = getWidth();
        if (row == null || row.length < width) {
            row = new byte[width];
        }
        System.arraycopy(luminances, y * width, row, 0, width);
        return row;
    }

    // Since this class does not support cropping, the underlying byte array
    // already contains
    // exactly what the caller is asking for, so give it to them without a copy.
    @Override
    public byte[] getMatrix() {
        return luminances;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凌摄,一起剝皮案震驚了整個濱河市羡蛾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锨亏,老刑警劉巖痴怨,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異器予,居然都是意外死亡浪藻,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門乾翔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爱葵,“玉大人,你說我怎么就攤上這事反浓∶日桑” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵雷则,是天一觀的道長辆雾。 經(jīng)常有香客問我,道長月劈,這世上最難降的妖魔是什么乾颁? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮艺栈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘湾盒。我一直安慰自己湿右,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布罚勾。 她就那樣靜靜地躺著毅人,像睡著了一般。 火紅的嫁衣襯著肌膚如雪尖殃。 梳的紋絲不亂的頭發(fā)上丈莺,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機與錄音送丰,去河邊找鬼缔俄。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的俐载。 我是一名探鬼主播蟹略,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼遏佣!你這毒婦竟也來了挖炬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤状婶,失蹤者是張志新(化名)和其女友劉穎意敛,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體膛虫,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡草姻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了走敌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碴倾。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖掉丽,靈堂內(nèi)的尸體忽然破棺而出跌榔,到底是詐尸還是另有隱情,我是刑警寧澤捶障,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布僧须,位于F島的核電站,受9級特大地震影響项炼,放射性物質(zhì)發(fā)生泄漏担平。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一锭部、第九天 我趴在偏房一處隱蔽的房頂上張望暂论。 院中可真熱鬧,春花似錦拌禾、人聲如沸取胎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽闻蛀。三九已至,卻和暖如春您市,著一層夾襖步出監(jiān)牢的瞬間觉痛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工茵休, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留薪棒,地道東北人手蝎。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像盗尸,于是被迫代替她去往敵國和親柑船。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,303評論 25 707
  • 興奮勁還沒過泼各,嚴苛的軍訓(xùn)便如約而至了鞍时。 操場上,大家排著隊扣蜻∧嫖。“八班,王教官莽使∪窦”主任分配著各班的教官,很快就念到了林...
    福氣喵閱讀 422評論 1 2
  • 如果你的朋友中,已經(jīng)有為人父母的話亿笤,一定會用以下同樣的感受: 朋友圈一言不合就曬娃睡覺要曬吃飯要曬去玩要曬而且必須...
    蜘蜘紡閱讀 285評論 0 0
  • 同事前天交了辭呈翎迁,昨天下午就坐飛機回到了家。問他為什么離開的這么突然净薛,回答汪榔,建筑行業(yè)太辛苦了,特別是一線管理人員肃拜。...
    燕coco閱讀 624評論 0 4