美文网首页native@IT·互联网程序员
react-native 实现条码扫描(ios&andr

react-native 实现条码扫描(ios&andr

作者: sybil052 | 来源:发表于2017-04-19 14:57 被阅读5861次

    从网上看了很多关于react native实现条码扫描的文章,里面出现了很多第三方库,有的文章说react-native-camera不能支持Android,我就自己试着写写,发现他可以兼容ios和Android。下面就来看看怎么实现吧~

    一、新建项目

    首先打开终端,在相应的目录下输入命令创建新项目

    react-native init CameraDemo
    

    项目创建完成,进入项目根目录下输入命令下载react-native-camera库

    1.npm install react-native-camera@https://github.com/lwansbrough/react-native-camera.git --save
    2.react-native link react-native-camera
    

    二、Android配置

    1.打开android/app/src/main/java/[...]/MainApplication.java文件,添加import com.lwansbrough.RCTCamera.RCTCameraPackage;,在getPackages()方法里添加new RCTCameraPackage(),
    2.打开android/settings.gradle文件,添加

    include ':react-native-camera'
    project(':react-native-camera').projectDir = new File(rootProject.projectDir,   '../node_modules/react-native-camera/android')
    

    3.打开android/app/build.gradle文件,在dependencies{}中添加compile project(':react-native-camera'
    4.在AndroidManifest.xml配置文件中添加相关权限:

    <uses-permission android:name="android.permission.CAMERA" />//相机权限
    <uses-permission android:name="android.permission.VIBRATE" />//震动权限
    <uses-feature android:name="android.hardware.camera" android:required="false" />
    <uses-feature android:name="android.hardware.camera.front" android:required="false" />
    

    注:android配置的前三个步骤一般情况下会自动添加,若没有添加,按上述步骤手动添加!

    三、iOS配置

    1.使用Xcode打开CameraDemo/ios/CameraDemo.xcodeproj文件,在Project navigator->Libraries文件夹上右击选择Add Files to 'CameraDemo';
    2.选择项目中的node_modules->react-native-camera并且添加RCTCamera.xcodeproj文件;
    3.在Build Phases中添加libRCTCamera.a;
    4.在Build Settings中找到Search Paths下的Header Search Paths,添加$(SRCROOT)/../../react-native/React和$(SRCROOT)/../../../React,并且选择recursive;
    如下图:


    QQ20170419-140900@2x.png

    5.打开ScanDemo/ios/ScanDemo/Info.plist文件,添加下列权限

    <key>NSCameraUsageDescription</key>
    <string>请允许使用您的相机</string>
    <key>NSLocationWhenInUseUsageDescription</key>
    <string></string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>请允许打开您的相册</string>
    

    注:ios中前三个步骤也会自动加载,若没有添加,需按上述步骤手动添加!

    四、编写代码

    1.scan.js代码

    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import {
        View,
        Text,
        StyleSheet,
        Image,
        Platform,
        Vibration,
        TouchableOpacity,
        Animated,
        Easing,
        Dimensions
    } from 'react-native';
    
    const {width, height}  = Dimensions.get('window');
    
    import {ToastMessage} from '../../utils/toast';
    import Camera from 'react-native-camera';
    import ViewFinder from '../../components/order/viewFinder';
    
    import backIcon from '../../../assets/img/backIcon.png';//返回按钮
    import scanLine from '../../../assets/img/scan_line.png';//扫描线
    
    export default class Scan extends Component {
        constructor(props) {
            super(props);
            this.camera = null;
            this.state = {
                transCode:'',//条码
                openFlash: false,
                active: true,
                flag:true,
                fadeInOpacity: new Animated.Value(0), // 初始值
                isEndAnimation:false,//结束动画标记
            }
            this._goBack = this._goBack.bind(this);
            this._startAnimation = this._startAnimation.bind(this);
            this.barcodeReceived = this.barcodeReceived.bind(this);
            this._search = this._search.bind(this);
            this._changeFlash = this._changeFlash.bind(this);
            this.changeState = this.changeState.bind(this);
        }
        componentDidMount() {
             this._startAnimation(false);
        }
        //开始动画,循环播放
        _startAnimation(isEnd) {
            Animated.timing(this.state.fadeInOpacity, {
                toValue: 1,
                duration: 3000,
                easing: Easing.linear
            }).start(
                 () => {
                     if (isEnd){
                         this.setState({
                             isEndAnimation:true
                         })
                         return;
                     }
                     if (!this.state.isEndAnimation){
                         this.state.fadeInOpacity.setValue(0);
                         this._startAnimation(false)
                     }
                 }
            );
            console.log("开始动画");
        }
         barcodeReceived(e) {
            if (e.data !== this.transCode) {
                Vibration.vibrate([0, 500, 200, 500]);
                this.transCode = e.data; // 放在this上,防止触发多次,setstate有延时
                if(this.state.flag){
                    this.changeState(false);
                    //通过条码编号获取数据
                }
                console.log("transCode="+this.transCode);
            }
        }
         //返回按钮点击事件
        _goBack() {
            this.setState({
                isEndAnimation:true,
            });
            this.props.navigator.pop();
        }
        //开灯关灯
        _changeFlash() {
            this.setState({
                openFlash: !this.state.openFlash,
            });
        }
         //改变请求状态
        changeState(status){
            this.setState({
                flag:status
            });
            console.log('status='+status);
        }
    
        render(){
            const {
                    openFlash,
                    active,
                } = this.state;
            return(
                <View style={styles.allContainer}>
                    {(() => {
                        if (active) {
                            return (
                                <Camera
                                    ref={cam => this.camera = cam}
                                    style={styles.cameraStyle}
                                    barcodeScannerEnabled={true}
                                    onBarCodeRead={
                                        this.barcodeReceived
                                    }
                                    torchMode={openFlash ? 'on' : 'off'}>
                                        <View style={styles.container}>
                                            <View style={styles.titleContainer}>
                                                <View style={styles.leftContainer}>
                                                    <TouchableOpacity activeOpacity={1} onPress={ this._goBack}>
                                                        <View>
                                                            <Image style={ styles.backImg } source={ backIcon }/>
                                                        </View>
                                                    </TouchableOpacity>
                                               </View>
                                            </View>
                                        </View>
                                    <View style={styles.centerContainer}/>
                                    <View style={{flexDirection:'row'}}>
                                        <View style={styles.fillView}/>
                                        <View style={styles.scan}>
                                            <ViewFinder/>
                                            <Animated.View style={[styles.scanLine, {
                                                opacity: 1,
                                                transform:[{
                                                    translateY:this.state.fadeInOpacity.interpolate({
                                                        inputRange:[0,1],
                                                        outputRange:[0,220]
                                                    })
                                                }]
                                            }]}>
                                                <Image source={scanLine}/>
                                            </Animated.View>
                                        </View>
                                        <View style={styles.fillView}/>
                                    </View>
                                    <View style={styles.bottomContainer}>
                                    <Text
                                        style={[
                                            styles.text,
                                            {
                                                textAlign: 'center',
                                                width: 220,
                                                marginTop: active ? 25 : 245,
                                            },
                                        ]}
                                        numberOfLines={2}
                                    >
                                        将运单上的条码放入框内即可自动扫描。
                                    </Text>
                                    <TouchableOpacity onPress={this._changeFlash}>
                                        <View style={styles.flash}>
                                            <Text style={styles.icon}></Text>
                                            <Text style={styles.text}>
                                                开灯/关灯
                                            </Text>
                                        </View>
                                    </TouchableOpacity>
                                    </View>
                                </Camera>
                             );
                        }
                    })()}
                </View>
            )
        }
    }
    
    const styles =StyleSheet.create({
        allContainer:{
            flex:1,
        },
        container: {
            ...Platform.select({
                ios: {
                    height: 64,
                },
                android: {
                    height: 50
                }
            }),
            backgroundColor:BLACK_COLOR,
            opacity:0.5
        },
        titleContainer: {
            flex: 1,
            ...Platform.select({
                ios: {
                    paddingTop: 15,
                },
                android: {
                    paddingTop: 0,
                }
            }),
            flexDirection: 'row',
        },
        leftContainer: {
            flex:0,
            justifyContent: 'center',
        },
        backImg: {
            marginLeft: 10,
        },
        cameraStyle: {
            alignSelf: 'center',
            width: width,
            height: height,
        },
        flash: {
            flexDirection: 'column',
            alignItems: 'center',
            justifyContent: 'flex-start',
            marginTop: 60,
        },
        flashIcon: {
            fontSize: 1,
            color: WHITE_COLOR,
        },
        text: {
            fontSize: 14,
            color: WHITE_COLOR,
            marginTop:5
        },
        icon:{
            color:WHITE_COLOR,
            fontSize:20,
            fontFamily:'iconfont'
        },
        scanLine:{
             alignSelf:'center',
        },
        centerContainer:{
            ...Platform.select({
                ios: {
                    height: 80,
                },
                android: {
                    height: 60,
                }
            }),
            width:width,
            backgroundColor:BLACK_COLOR,
            opacity:0.5
        },
        bottomContainer:{
            alignItems:'center',
            backgroundColor:BLACK_COLOR,
            alignSelf:'center',
            opacity:0.5,
            flex:1,
            width:width
        },
        fillView:{
            width: (width-220)/2,
            height: 220,
            backgroundColor: BLACK_COLOR,
            opacity: 0.5
        },
        scan:{
            width: 220,
            height: 220,
            alignSelf: 'center'
        }
    
    })      
    

    2.viewFinder.js代码

    import React, {
      Component,
      PropTypes,
    } from 'react';
    import {
      ActivityIndicator,
      StyleSheet,
      View,
    } from 'react-native';
    
    const styles = StyleSheet.create({
      container: {
        alignItems: 'center',
        justifyContent: 'center',
        position: 'absolute',
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
      },
      viewfinder: {
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: 'transparent',
      },
      topLeftEdge: {
        position: 'absolute',
        top: 0,
        left: 0,
      },
      topRightEdge: {
        position: 'absolute',
        top: 0,
        right: 0,
      },
      bottomLeftEdge: {
        position: 'absolute',
        bottom: 0,
        left: 0,
      },
      bottomRightEdge: {
        position: 'absolute',
        bottom: 0,
        right: 0,
      },
    });
    
    class Viewfinder extends Component {
      constructor(props) {
        super(props);
    
        this.getBackgroundColor = this.getBackgroundColor.bind(this);
        this.getSizeStyles = this.getSizeStyles.bind(this);
        this.getEdgeSizeStyles = this.getEdgeSizeStyles.bind(this);
        this.renderLoadingIndicator = this.renderLoadingIndicator.bind(this);
      }
    
      getBackgroundColor() {
        return ({
          backgroundColor: this.props.backgroundColor,
        });
      }
    
      getEdgeColor() {
        return ({
          borderColor: this.props.color,
        });
      }
      
      getSizeStyles() {
        return ({
          height: this.props.height,
          width: this.props.width,
        });
      }
    
      getEdgeSizeStyles() {
        return ({
          height: this.props.borderLength,
          width: this.props.borderLength,
        });
      }
    
      renderLoadingIndicator() {
        if (!this.props.isLoading) {
          return null;
        }
    
        return (
          <ActivityIndicator
            animating={this.props.isLoading}
            color={this.props.color}
            size="large"
          />
        );
      }
    
    render() {
        return (
          <View style={[styles.container, this.getBackgroundColor()]}>
            <View style={[styles.viewfinder, this.getSizeStyles()]}>
              <View
                style={[
                  this.getEdgeColor(),
                  this.getEdgeSizeStyles(),
                  styles.topLeftEdge,
                  {
                    borderLeftWidth: this.props.borderWidth,
                    borderTopWidth: this.props.borderWidth,
                  },
                ]}
              />
              <View
                style={[
                  this.getEdgeColor(),
                  this.getEdgeSizeStyles(),
                  styles.topRightEdge,
                  {
                    borderRightWidth: this.props.borderWidth,
                    borderTopWidth: this.props.borderWidth,
                  },
                ]}
              />
              {this.renderLoadingIndicator()}
              <View
                style={[
                 this.getEdgeColor(),
                  this.getEdgeSizeStyles(),
                  styles.bottomLeftEdge,
                  {
                    borderLeftWidth: this.props.borderWidth,
                    borderBottomWidth: this.props.borderWidth,
                  },
                ]}
              />
              <View
                style={[
                  this.getEdgeColor(),
                  this.getEdgeSizeStyles(),
                  styles.bottomRightEdge,
                  {
                    borderRightWidth: this.props.borderWidth,
                    borderBottomWidth: this.props.borderWidth,
                  },
                ]}
              />
            </View>
          </View>
       );
      }
    }
    
    Viewfinder.propTypes = {
      backgroundColor: PropTypes.string,
      borderWidth: PropTypes.number,
      borderLength: PropTypes.number,
      color: PropTypes.string,
      height: PropTypes.number,
      isLoading: PropTypes.bool,
      width: PropTypes.number,
    };
    
    Viewfinder.defaultProps = {
      backgroundColor: 'transparent',
      borderWidth: 3,
      borderLength: 20,
      color: COLOR_MAIN,
      height: 220,
      isLoading: false,
      width: 220,
    };
    module.exports = Viewfinder;
    

    大功告成~最后上张效果图:

    1866D1330B2C6E2F40A40A7E7246C6BD.jpg

    最近很多简友问我资源在哪下载,我就把代码整理了一下,发到了github上,现附上地址CameraDemo,以便大家下载~

    相关文章

      网友评论

        本文标题:react-native 实现条码扫描(ios&andr

        本文链接:https://www.haomeiwen.com/subject/wzlqzttx.html