React-Native+react-native-camera+react-native-image-picker实现扫码功能、拍照显示及从拍好的照片中读取二维码或条形码

一、用到技术版本

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://[email protected]/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(
             
                
                    {this.state.textval==null?this.state.tabTitle:this.state.textval}
                选择照片
                
                this.beginSaoMiao()}>
                    获取图片中二维码或者条形码信息
                
                this.clickSaoMiao()}>
                    开始扫描
                
                {this.state.viewAppear ?
                     
                         {
                                this.camera = ref;
                            }}
                            style={styles.preview}
                            type={RNCamera.Constants.Type.back}
                            flashMode={RNCamera.Constants.FlashMode.on}
                            onBarCodeRead={this.onBarCodeRead}
                        >
                            
                                
                                
                                将二维码放入框内,即可自动扫描
                            
                        
                    : null
                    }
            
        );
    }
}
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 VALID_BARCODE_TYPES =
            Collections.unmodifiableMap(new HashMap() {
                {
                    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 hints = new EnumMap<>(DecodeHintType.class);
                    EnumSet decodeFormats = EnumSet.noneOf(BarcodeFormat.class);
                    for(int i=0;i> 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;
        }
    }

}

上传几张示例图

React-Native+react-native-camera+react-native-image-picker实现扫码功能、拍照显示及从拍好的照片中读取二维码或条形码_第1张图片React-Native+react-native-camera+react-native-image-picker实现扫码功能、拍照显示及从拍好的照片中读取二维码或条形码_第2张图片

React-Native+react-native-camera+react-native-image-picker实现扫码功能、拍照显示及从拍好的照片中读取二维码或条形码_第3张图片

 

你可能感兴趣的:(React-Native+react-native-camera+react-native-image-picker实现扫码功能、拍照显示及从拍好的照片中读取二维码或条形码)