React-Native 掃碼功能以及從相冊讀取二維碼

我使用的技術棧是:react-native(0.57.8)+ react-navigation + react-redux + ant-design + axios


我在做的ReactNative項目需要實現掃碼功能以及從相冊讀取本地二維碼的功能

我實現這兩個功能使用的插件有

掃碼功能

掃描頁面黑屏

首先是使用react-native-camera完成掃碼功能,根據git文檔進行安裝和配置,攝像頭處理權限等配置不再贅述,遇到問題可以在Issue中看看是否有解決。下面稱我實現的掃描頁面爲Scan頁。
](https://img-blog.csdnimg.cn/20190302122736533.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzMxMjMxOTU1,size_16,color_FFFFFF,t_70)
我遇到的第一個問題是重複進入Scan頁,相機加載有問題,會出現黑屏現象。解決辦法是,根據navigation判斷,當進入頁面時,再顯示RNCamera.

componentDidMount() {
    const { navigation } = this.props;
    navigation.addListener("willFocus", () =>
      this.setState({ focusedScreen: true })
    );
    navigation.addListener("willBlur", () =>
      this.setState({ focusedScreen: false, showModal: false })
    );
  }

// render函數中:
this.state.focusedScreen && RNCameraComponent

相機佈局

第二個問題是相機佈局,設計給的圖是像微信的二維碼掃描一樣,中間有一塊透明區域做掃碼。RNCamera無法設置寬高,視圖是全屏,需要自己佈局。我將頁面分爲了上中下三塊,中間又分爲左中右三塊,大概是這樣:
在這裏插入圖片描述
中間②區域背景用UI給的透明圖片,加上掃描動畫,就像模像樣啦~
掃描動畫用的是RN原生的Animated

// 初始化
 this.state = {
      moveAnim: new Animated.Value(0), // 掃描動畫
};

// 設置掃描高度,速度等
startAnimation = () => {
    this.state.moveAnim.setValue(258);
    Animated.timing(this.state.moveAnim, {
      toValue: 0,
      duration: 1500,
      easing: Easing.linear
    }).start(() => this.startAnimation());
  };
  
// render函數中
  <Animated.View
      style={[styles.border,
      { transform: [{ translateY:this.state.moveAnim }] }
      ]}
    />

完成了相機佈局,將onBarCodeRead函數寫好自己的回調,掃描二維碼部分就告一段落了。

相冊讀取二維碼

這一部分我用了react-native-image-pickerreact-native-local-barcode-recognizer,首先用react-native-image-picker讀取相冊並選取想要掃描的圖片,再將圖片信息使用react-native-local-barcode-recognizer解析,其實解析部分也可以自己使用Zxing庫,react-native-local-barcode-recognizer本身也是對Zxing的封裝。

引入兩個庫的過程git上有,很方便的一點是image-picker讀取圖片數據可以直接獲取Base64數據,格式剛好是local-barcode-recognizer解析需要的。

部分二維碼無法識別

local-barcode-recognizer庫解析時,有些二維碼無法識別,我剛開始一直以爲是二維碼數據過長,不好解析,後來發現是現在的手機拍照像素高照片過大。於是在local-barcode-recognizer庫的源碼解析代碼中加入一個縮小圖片數據的函數。果然,縮小圖片後可以解析之前拍下的二維碼啦~

public static Bitmap getSmallerBitmap(Bitmap bitmap) {
        int size = bitmap.getWidth() * bitmap.getHeight() / 160000;
        if (size <= 1)
            return bitmap; // 如果小於
        else {
            Matrix matrix = new Matrix();
            matrix.postScale((float) (1 / Math.sqrt(size)), (float) (1 / Math.sqrt(size)));
            Bitmap resizeBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix,
                    true);
            return resizeBitmap;
        }
    }

二維碼識別率低

二維碼識別率低的問題困擾了我很久,畢竟用react-native-camera掃描的時候,很快就識別了相應二維碼。
我找了很久原因,發現react-native-camera解析時用的格式是YUV,而react-native-local-barcode-recognizer中用的是ARGB
網上也有博客說YUV格式解析識別率會更高,於是我更改了react-native-local-barcode-recognizer源碼中decode的函數,確實提高了一定的識別率。(但是還是達不到react-native-camera的程度,好想直接知道微信的識別算法哦)

private BinaryBitmap generateBitmapFromImageData(Bitmap bitmap) {
        bitmap = getSmallerBitmap(bitmap);
        int[] mImageData = new int[bitmap.getWidth() * bitmap.getHeight()];
        bitmap.getPixels(mImageData, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
        int inputWidth = bitmap.getWidth();
        int inputHeight = bitmap.getHeight();
        byte[] yuv = new byte[inputWidth * inputHeight + ((inputWidth % 2 == 0 ? inputWidth : (inputWidth + 1))
                * (inputHeight % 2 == 0 ? inputHeight : (inputHeight + 1))) / 2];
        encodeYUV420SP(yuv, mImageData, inputWidth, inputHeight);
        bitmap.recycle();
        PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(yuv, // byte[] yuvData
                inputWidth, // int dataWidth
                inputHeight, // int dataHeight
                0, // int left
                0, // int top
                inputWidth, // int width
                inputHeight, // int height
                false // boolean reverseHorizontal
        );
        return new BinaryBitmap(new);
    }

    private static void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {
        // 幀圖片的像素大小
        final int frameSize = width * height;
        // Y的index從0開始
        int yIndex = 0;
        // UV的index從frameSize開始
        int uvIndex = frameSize;
        // YUV數據, ARGB數據
        int Y, U, V, a, R, G, B;
        ;
        int argbIndex = 0;
        // ---循環所有像素點,RGB轉YUV---
        for (int j = 0; j < height; j++) {
            for (int i = 0; i < width; i++) {

                // a is not used obviously
                a = (argb[argbIndex] & 0xff000000) >> 24;
                R = (argb[argbIndex] & 0xff0000) >> 16;
                G = (argb[argbIndex] & 0xff00) >> 8;
                B = (argb[argbIndex] & 0xff);
                argbIndex++;

                // well known RGB to YUV algorithm
                Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
                U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
                V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;

                Y = Math.max(0, Math.min(Y, 255));
                U = Math.max(0, Math.min(U, 255));
                V = Math.max(0, Math.min(V, 255));
                yuv420sp[yIndex++] = (byte) Y;
                // ---UV---
                if ((j % 2 == 0) && (i % 2 == 0)) {
                    yuv420sp[uvIndex++] = (byte) V;
                    yuv420sp[uvIndex++] = (byte) U;
                }
            }
        }
    }

小提示

如果你像我一樣直接修改 node_modules 內依賴源碼,記得重新發包哦。因爲線上自動化構建時,拉取的是未經修改的依賴。

我的解決辦法是:fork 依賴的 git 源碼,修改後發佈新的npm包,再link 到業務項目中。

以上就是我使用React-Native 實現掃描二維碼及讀取相冊二維碼的小結,感謝閱讀。

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