一、用到技术版本
1、react-native(0.59.8)
2、Android:buildToolsVersion = "28.0.3",minSdkVersion = 16,compileSdkVersion = 28,targetSdkVersion = 28,supportLibVersion = "28.0.0"
二、具体实现
react使用
- react-native-camera (npm install --save react-native-camera@git+https://git@github.com/react-native-community/react-native-camera.git , react-native link react-native-camera)
- react-native-image-picker (npm install react-native-image-picker --save , react-native link react-native-image-picker)
- react-native-local-barcode-recognizer (npm install react-native-local-barcode-recognizer --save , react-native link react-native-local-barcode-recognizer) 备选(npm install rn-local-qrdecode,react-native link rn-local-qrdecode)
import React, { Component } from 'react';
import {
AppRegistry,
Navigator,
TouchableOpacity,
StyleSheet,
Text,
View,
Image,
Platform,
Animated,
Easing,
} from 'react-native';
import { RNCamera } from 'react-native-camera'
import LocalBarcodeRecognizer from 'react-native-local-barcode-recognizer';
//图片选择器
var ImagePicker = require('react-native-image-picker');
//图片选择器参数设置
var options = {
title: '请选择图片来源',
cancelButtonTitle:'取消',
takePhotoButtonTitle:'拍照',
chooseFromLibraryButtonTitle:'相册图片',
storageOptions: {
skipBackup: true,
path: 'images'
}
};
const imageBase64 = "data:image/jpeg;base64,";
export default class electronicsOrder extends Component{
constructor(props){
super(props);
// 初始化数据源
this.state={
tabTitle : this.props.tabLabel,
avatarSource: null,
textval:null,
sourceData:null,
viewAppear:false,
moveAnim: new Animated.Value(0)
}
}
componentDidMount() {
//进入后直接开始扫描
//this.startAnimation();
}
startAnimation = () => {
this.state.moveAnim.setValue(0);
Animated.timing(
this.state.moveAnim,
{
toValue: -200,
duration: 1500,
easing: Easing.linear
}
).start(() => this.startAnimation());
};
// 识别二维码
onBarCodeRead = (result) => {
const { navigate } = this.props.navigation;
const {data} = result;
if(data!=null && data!=''){
this.setState({textval:data,viewAppear:false},function(){
});
}
//如果要跳转地址使用下面的
// navigate('createOrder', {
// url: data
// })
};
//选择照片按钮点击
choosePic() {
ImagePicker.showImagePicker(options, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('用户取消了选择!');
}
else if (response.error) {
alert("ImagePicker发生错误:" + response.error);
}
else if (response.customButton) {
alert("自定义按钮点击:" + response.customButton);
}
else {
let source = { uri: response.uri };
// You can also display the image using data:
// let source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
avatarSource: source,
sourceData:'data:image/jpeg;base64,' + response.data
});
}
});
}
beginSaoMiao(){
if(null!=this.state.avatarSource){
this.recoginze();
}
}
recoginze = async ()=>{
// Here is the demoe
let result = await LocalBarcodeRecognizer.decode(this.state.sourceData.replace("data:image/jpeg;base64,",""),{codeTypes:['ean13','qr']});
alert(result);
}
clickSaoMiao(){
this.setState({viewAppear: true},function(){
this.startAnimation();
});
}
render(){
return(
<View style={styles.container}>
<Text style={[{fontSize:20},styles.red]}>
{this.state.textval==null?this.state.tabTitle:this.state.textval}</Text>
<Text style={styles.item} onPress={this.choosePic.bind(this)}>选择照片</Text>
<Image source={this.state.avatarSource} style={styles.image} />
<TouchableOpacity onPress={()=>this.beginSaoMiao()}>
<Text style={{fontSize:20,alignItems:'center'}}>获取图片中二维码或者条形码信息</Text>
</TouchableOpacity>
<TouchableOpacity onPress={()=>this.clickSaoMiao()}>
<Text style={{fontSize:20,alignItems:'center'}}>开始扫描</Text>
</TouchableOpacity>
{this.state.viewAppear ?
<View style={{flex: 1, backgroundColor: 'black',}}>
<RNCamera
ref={ref => {
this.camera = ref;
}}
style={styles.preview}
type={RNCamera.Constants.Type.back}
flashMode={RNCamera.Constants.FlashMode.on}
onBarCodeRead={this.onBarCodeRead}
>
<View style={styles.rectangleContainer}>
<View style={styles.rectangle}/>
<Animated.View style={[
styles.border,
{transform: [{translateY: this.state.moveAnim}]}]}/>
<Text style={styles.rectangleText}>将二维码放入框内,即可自动扫描</Text>
</View>
</RNCamera>
</View>: null
}
</View>
);
}
}
const styles = StyleSheet.create({
center:{
flex:1,
justifyContent:'center',
alignItems:'center',
},
red:{
color:'#f00',
},
container:{
flex: 1,
// marginTop:25,
backgroundColor: '#F5FCFF',
// justifyContent: 'center',
// alignItems: 'center',
// flexDirection: 'row'
},
item:{
margin:15,
height:30,
borderWidth:1,
padding:6,
borderColor:'#ddd',
textAlign:'center'
},
image:{
height:198,
width:300,
alignSelf:'center',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center'
},
rectangleContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'transparent'
},
rectangle: {
height: 200,
width: 200,
borderWidth: 1,
borderColor: '#00FF00',
backgroundColor: 'transparent'
},
rectangleText: {
flex: 0,
color: '#fff',
marginTop: 10
},
border: {
flex: 0,
width: 200,
height: 2,
backgroundColor: '#00FF00',
}
});
Android
1) react-native-camera 需要修改 android目录下build.gradle文件中allprojects的repositories信息 加入如下代码
maven {
url 'https://maven.google.com'
}
maven {
url "https://jitpack.io"
}
allprojects {
repositories {
mavenLocal()
google()
jcenter()
maven {
url 'https://maven.google.com'
}
maven {
url "https://jitpack.io"
}
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
}
}
继续修改android/app/build.gradle文件
在defaultConfig中加入missingDimensionStrategy 'react-native-camera', 'general' 。因为加入三个第三方包导致android执行数量超过65536,所以在defaultConfig中继续加入如下代码:multiDexEnabled true
例如
defaultConfig {
applicationId "com.demo"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
missingDimensionStrategy 'react-native-camera', 'general'
multiDexEnabled true
}
2)react-native-local-barcode-recognizer
因为默认版本与我使用的不一致 我修改了其源码版本配置如下,
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1' //修改为一直3.3.1
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
lintOptions {
abortOnError false
}
sourceSets {
main {
aidl.srcDirs = ['src/main/java']
}
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'com.facebook.react:react-native:+'
implementation "com.google.zxing:core:3.3.0"
}
因为扫描识别率较低,有些还不能识别所以我参照原作者cicistream 的代码进行了如下修改 ,可以直接覆盖整个文件
文件路径android/src/main/java/cn.jystudio.local.barcode.recognizer.LocalBarcodeRecognizerModule.java
完整代码:
package cn.jystudio.local.barcode.recognizer;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.util.Base64;
import com.facebook.react.bridge.*;
import com.google.zxing.*;
import com.google.zxing.common.HybridBinarizer;
import java.util.*;
public class LocalBarcodeRecognizerModule extends ReactContextBaseJavaModule {
public static final String BARCODE_CODE_TYPE_KEY="codeTypes";
public static final Map<String, Object> VALID_BARCODE_TYPES =
Collections.unmodifiableMap(new HashMap<String, Object>() {
{
put("aztec", BarcodeFormat.AZTEC.toString());
put("ean13", BarcodeFormat.EAN_13.toString());
put("ean8", BarcodeFormat.EAN_8.toString());
put("qr", BarcodeFormat.QR_CODE.toString());
put("pdf417", BarcodeFormat.PDF_417.toString());
put("upc_e", BarcodeFormat.UPC_E.toString());
put("datamatrix", BarcodeFormat.DATA_MATRIX.toString());
put("code39", BarcodeFormat.CODE_39.toString());
put("code93", BarcodeFormat.CODE_93.toString());
put("interleaved2of5", BarcodeFormat.ITF.toString());
put("codabar", BarcodeFormat.CODABAR.toString());
put("code128", BarcodeFormat.CODE_128.toString());
put("maxicode", BarcodeFormat.MAXICODE.toString());
put("rss14", BarcodeFormat.RSS_14.toString());
put("rssexpanded", BarcodeFormat.RSS_EXPANDED.toString());
put("upc_a", BarcodeFormat.UPC_A.toString());
put("upc_ean", BarcodeFormat.UPC_EAN_EXTENSION.toString());
}
});
public LocalBarcodeRecognizerModule(ReactApplicationContext reactContext) {
super(reactContext);
}
/**
* @return the name of this module. This will be the name used to {@code require()} this module
* from javascript.
*/
@Override
public String getName() {
return "LocalBarcodeRecognizer";
}
@ReactMethod
public void decode(String base64Data, ReadableMap options, final Promise p){
try {
byte[] decodedString = Base64.decode(base64Data,Base64.DEFAULT);
Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
Result result = null;
MultiFormatReader reader = new MultiFormatReader();
if(options.hasKey(BARCODE_CODE_TYPE_KEY)){
ReadableArray codeTypes = options.getArray(BARCODE_CODE_TYPE_KEY);
if(codeTypes.size()>0) {
EnumMap<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
EnumSet<BarcodeFormat> decodeFormats = EnumSet.noneOf(BarcodeFormat.class);
for(int i=0;i<codeTypes.size();i++){
String code = codeTypes.getString(i);
String formatString = (String) VALID_BARCODE_TYPES.get(code);
if(formatString!=null){
decodeFormats.add(BarcodeFormat.valueOf(formatString));
}
}
hints.put(DecodeHintType.POSSIBLE_FORMATS,decodeFormats);
reader.setHints(hints);
}
}
try {
BinaryBitmap bitmap = generateBitmapFromImageData(decodedByte);
result = reader.decode(bitmap);
} catch (NotFoundException e) {
BinaryBitmap bitmap = generateBitmapFromImageData(rotateImage(decodedByte,90));
try {
result = reader.decode(bitmap);
} catch (NotFoundException e1) {
//no barcode Found
}
} catch (Throwable t) {
t.printStackTrace();
}
p.resolve(result!=null?result.getText():"");
}catch (Exception e){
p.reject(e);
}
}
// private BinaryBitmap generateBitmapFromImageData(Bitmap bitmap) {
// int[] mImageData = new int[bitmap.getWidth()*bitmap.getHeight()];
// bitmap.getPixels(mImageData, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
// LuminanceSource source = new RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(),mImageData);
// return new BinaryBitmap(new HybridBinarizer(source));
// }
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 HybridBinarizer(source));
}
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;
}
}
}
}
private Bitmap rotateImage(Bitmap src, float degree)
{
// create new matrix
Matrix matrix = new Matrix();
// setup rotation degree
matrix.postRotate(degree);
Bitmap bmp = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
return bmp;
}
private 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;
}
}
}
上传几张示例图