从网上看了很多关于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;
如下图:
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;
大功告成~最后上张效果图:
最近很多简友问我资源在哪下载,我就把代码整理了一下,发到了github上,现附上地址CameraDemo,以便大家下载~