從0到1打造一款react-native App(一)環境配置
從0到1打造一款react-native App(二)Navigation+Reduxreact
項目地址:https://github.com/jiwenjiang...android
拍照的主要需求是在拍照後,不將照片在系統相冊中顯示出來,android拍照後會默認存儲在DCIM文件夾當中,而此次主要須要作的就是把照片放在自定義的文件夾當中。git
拍照的第三方包有不少,好比react-native-image-picker,這個調用的是系統相機,用法比較簡單,可是拓展性較差,無論是此次項目主要的需求(拍照後不在系統相冊顯示),仍是自己拍照時的一些定製化的需求,相似微信拍照那種,都不容易實現,所以選擇了react-native-camera。github
最新版的react-native-camera(v 1.1.x)已經支持了人臉識別,文字識別等功能,仍是很強大的,這些功能可能往後都會用獲得,不過由於一些版本和平臺的緣由以後會換成expo的camera,這裏暫時仍是介紹rn的camera(v 0.7)。segmentfault
組件二次封裝:react-native
import React, { Component } from 'react'; import { Dimensions, StyleSheet, Button, Text, ImageBackground, View, TouchableOpacity } from 'react-native'; import Camera from 'react-native-camera'; import Icon from 'react-native-vector-icons/MaterialIcons'; import { deleteFile, mkdir, readPath } from '../../service/utils/fileOperations'; import RNFS from 'react-native-fs'; import moment from 'moment/moment'; class RNCamera extends Component { constructor(props) { super(props); this.state = { hidden: false, currentImage: null }; } async takePicture() { const options = {}; const { path: currentImage } = await this.camera.capture({ metadata: options }); this.setState({ currentImage }); } back() { this.setState({ currentImage: null, hidden: true }); } async check() { const [date, unixTime] = [moment().format('YYYY/MM/DD'), moment().unix()]; const dir = `${RNFS.DocumentDirectoryPath}/photo/${date}`; await mkdir(dir); const url = `${dir}/${unixTime}.jpg`; await RNFS.moveFile(this.state.currentImage, url); console.log(await readPath(dir)); this.setState({ currentImage: null }); } cancel() { deleteFile(this.state.currentImage); this.setState({ currentImage: null }); } render() { const { currentImage, hidden } = this.state; return ( <View style={[styles.container, hidden && styles.hidden]}> {currentImage ? <ImageBackground style={styles.photo} source={{ uri: currentImage }}> <TouchableOpacity style={styles.capture} onPress={() => this.cancel()}> <Icon name="close" size={30}/> </TouchableOpacity > <TouchableOpacity style={styles.capture} onPress={() => this.check()}> <Icon name="check" size={30}/> </TouchableOpacity > </ImageBackground > : <Camera ref={(cam) => { this.camera = cam; }} style={styles.preview} aspect={Camera.constants.Aspect.fill} captureTarget={Camera.constants.CaptureTarget.temp} > <TouchableOpacity style={styles.capture} onPress={() => this.back()}> <Icon name="expand-more" size={30}/> </TouchableOpacity > <TouchableOpacity style={styles.capture} onPress={() => this.takePicture()}> <Icon name="camera-alt" size={30}/> </TouchableOpacity > </Camera > } </View > ); } } const styles = StyleSheet.create( { container: { flex: 1, flexDirection: 'row' }, preview: { flex: 1, justifyContent: 'center', flexDirection: 'row', alignItems: 'flex-end' }, capture: { flex: 0, backgroundColor: 'rgba(255, 255, 255, 0.3)', borderRadius: 25, margin: 20, marginBottom: 30, width: 50, height: 50, alignItems: 'center', justifyContent: 'center', zIndex: 1 }, photo: { flex: 1, justifyContent: 'center', flexDirection: 'row', alignItems: 'flex-end' }, hidden: { display: 'none' } } ); export default RNCamera;
沒有對react-native-camera作過多的配置,須要注意的配置是captureTarget屬性。在v0.7版本的camera當中,captureTarget的可選配置項有4種。微信
實現基本思路是,經過外層調用來控制整個組件的樣式值,來管理組件的顯示與隱藏,即組件state的hidden屬性。當組件被成功調用顯示時,組件主要分爲兩塊,拍照和預覽。給定一個拍照照片的路徑值,即組件state的currentImage,若是當前組件存在一個照片的存儲路徑,就顯示預覽界面,如不存在就顯示拍照界面。而currentImage的值經過拍照成功的Promise或者取消的狀態去控制建立與刪除。async
拍照時去建立currentImagepost
async takePicture() { const options = {}; const { path: currentImage } = await this.camera.capture({ metadata: options }); this.setState({ currentImage }); }
隱藏組建,返回調用界面性能
back() { this.setState({ currentImage: null, hidden: true }); }
拍照完成後預覽照片及確認存儲
async check() { const [date, unixTime] = [moment().format('YYYY/MM/DD'), moment().unix()]; const dir = `${RNFS.DocumentDirectoryPath}/photo/${date}`; await mkdir(dir); const url = `${dir}/${unixTime}.jpg`; await RNFS.moveFile(this.state.currentImage, url); console.log(await readPath(dir)); this.setState({ currentImage: null }); }
存儲這裏用到了react-native-fs,這個第三方包就不過多介紹了,都是一些基礎的文件操做,比較好理解。經過在文件路徑下新建photo/xxxx-xx-xx的文件夾,確保天天拍攝的照片存放在當日的文件夾,方便後續的文件預覽時的篩選。在照片拍攝完畢後,react-native-camera會將拍攝的照片存放至臨時文件夾,而這裏須要作的就是將臨時文件夾的照片移動至咱們的目標文件夾,這裏順便說一下,文件move操做的性能是優於read+write的,這裏切記用move。關於android文件存儲這裏推薦一篇介紹的比較詳細的文章https://juejin.im/post/58b557...。
拍照完成後預覽照片及放棄存儲
cancel() { deleteFile(this.state.currentImage); this.setState({ currentImage: null }); }
操做預覽:
<View style={styles.container}> <Image style={styles.photo} source={{ uri: `file://${files[0].path}` }} //顯示第一張照片 /> </View >
在照片回顯時,檢測文件夾,讀取照片
const mkdir = async (url) => { const dirExists = await RNFS.exists(url); if (dirExists) { return new Promise(resolve => resolve(dirExists)); } await RNFS.mkdir(url); return new Promise(resolve => resolve(url)); }; async function storageFile() { const date = moment().format('YYYY/MM/DD'); const url = `${RNFS.DocumentDirectoryPath}/photo/${date}`; await mkdir(url); const files = await readPath(url); return files; }
react-native-camera支持對各類條形碼的掃描識別,主要的屬性有兩個
barCodeTypes={[Camera.constants.BarCodeType.qr]} //掃碼的類型
onBarCodeRead={this.props.onScanResultReceived} //掃碼成功後的回調
項目這裏直接把https://www.jianshu.com/p/347... 這篇文章中二次封裝好的一個二維碼掃描的組件複製了過來。主要是視圖層的二次封裝,有興趣的同窗也能夠本身封裝。
以後會把react-native-camera替換成expo中的camera,換完以後會繼續在這篇camera的文章中更新,也歡迎正在學習的同窗一塊兒交流~