我使用的技術棧是:react-native(0.57.8)+ react-navigation + react-redux + ant-design + axios
我在做的ReactNative項目需要實現掃碼功能以及從相冊讀取本地二維碼的功能
我實現這兩個功能使用的插件有
掃碼功能
掃描頁面黑屏
首先是使用react-native-camera完成掃碼功能,根據git文檔進行安裝和配置,攝像頭處理權限等配置不再贅述,遇到問題可以在Issue中看看是否有解決。下面稱我實現的掃描頁面爲Scan頁。
我遇到的第一個問題是重複進入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-picker和react-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 實現掃描二維碼及讀取相冊二維碼的小結,感謝閱讀。