先貼一波代碼
/**
* 錄音與播放
*/
import React, {Component} from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity,
Platform,
Slider,
ToastAndroid,
Alert,
Dimensions,
BackHandler
} from 'react-native';
import {AudioRecorder, AudioUtils} from 'react-native-audio';
import Sound from 'react-native-sound';
import Svg from "react-native-svg";
import DateUtil from '@utils/DateUtil';
import FileUtil from '@utils/FileUtil';
import {connect} from "react-redux";
import IconLib from "../../assets/svg/IconLib";
const {width} = Dimensions.get('window');
class SoundRecording extends Component {
constructor(props) {
super(props);
this.state = {
currentTime: 0,//
duration: 0,//總時長
recording: false,//是否錄音
paused: false,//是否暫停
stoppedRecording: false,
finished: false,
play: false,
audioPath: AudioUtils.MusicDirectoryPath + this.getAacName(),//生成的錄音
hasPermission: undefined,
};
}
static navigationOptions = ({navigation, navigationOptions}) => {
navigationOptions.header = null;
return {
...navigationOptions
};
}
prepareRecordingPath(audioPath) {
AudioRecorder.prepareRecordingAtPath(audioPath, {
SampleRate: 22050,
Channels: 1,
AudioQuality: "Low",
AudioEncoding: "aac",
AudioEncodingBitRate: 32000
});
}
/**
* 獲取錄音文件名
* @returns {string}
*/
getAacName() {
return "/" + DateUtil.getYMDHms() + ".aac";
}
componentWillMount() {
AudioRecorder.removeListeners();
}
handleBackPress = () => {
this._quitRandom();
return false;
}
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
AudioRecorder.requestAuthorization().then((isAuthorised) => {
this.setState({hasPermission: isAuthorised});
if (!isAuthorised) return;
this.prepareRecordingPath(this.state.audioPath);
AudioRecorder.onProgress = (data) => {
this.setState({currentTime: Math.floor(data.currentTime)});
};
AudioRecorder.onFinished = (data) => {
console.log("data" + JSON.stringify(data));
if (Platform.OS === 'ios') {
this._finishRecording(data.status === "OK", data.audioFileURL, data.audioFileSize);
}
};
});
}
/**
* 暫停錄音
* @returns {Promise<void>}
* @private
*/
async _pause() {
if (!this.state.recording) {
console.warn('Can\'t pause, not recording!');
return;
}
try {
const filePath = await AudioRecorder.pauseRecording();
this.setState({paused: true});
} catch (error) {
console.error(error);
}
}
/**
* 恢復(fù)錄音
* @returns {Promise<void>}
* @private
*/
async _resume() {
if (!this.state.paused) {
console.warn('Can\'t resume, not paused!');
return;
}
try {
await AudioRecorder.resumeRecording();
this.setState({paused: false});
} catch (error) {
console.error(error);
}
}
/**
* 刪除錄音,后臺主動停止錄音再刪除
* @returns {Promise<void>}
* @private
*/
async _deleteRandom() {
if (this.state.recording) {
await AudioRecorder.pauseRecording();
this.setState({paused: true, stoppedRecording: false})
}
Alert.alert('溫馨提示', '確認(rèn)刪除錄音嗎?',
[
{
text: "是", onPress: () => {
AudioRecorder.stopRecording();
this.setState({
finished: false,
currentTime: 0,
stoppedRecording: true,
paused:false,
duration: 0,
recording: false
})
}
},
{text: "否"}
])
}
/**
* 刪除錄音采盒,釋放資源
* @returns {Promise<void>}
* @private
*/
async _delete() {
if (this.state.recording) {
ToastAndroid.show("無法刪除软免,正在錄音", ToastAndroid.SHORT);
console.log('Can\'t delete, recording!');
return;
}
if (this.state.play) {
ToastAndroid.show("無法刪除贰军,正在播放", ToastAndroid.SHORT);
console.log('Can\'t delete, playing!');
return;
}
if (!this.state.stoppedRecording) {
ToastAndroid.show("無錄音", ToastAndroid.SHORT);
return;
}
var flag = await FileUtil.existsFile(this.state.audioPath);
if (!flag) {
ToastAndroid.show("無錄音", ToastAndroid.SHORT);
console.log('no file');
return;
}
Alert.alert('溫馨提示', '確認(rèn)刪除錄音嗎?',
[
{
text: "是", onPress: () => {
FileUtil.deleteFile(this.state.audioPath);
this.setState({currentTime: 0.0, duration: 0}, () => {
this.sound.release()
});
}
},
{text: "否"}
])
}
/**
* 暫停播放
* @private
*/
_pausePlay = () => {
if (this.sound) {
this.sound.pause();
}
this.setState({play: false});
}
/**
* 完成錄音
* @returns {Promise<void>}
* @private
*/
async _stop() {
if (!this.state.recording) {
console.warn('Can\'t stop, not recording!');
return;
}
try {
const filePath = await AudioRecorder.stopRecording();
this.sound = new Sound(this.state.audioPath, '', (error) => {
if (error) {
console.log('failed to load the sound', error);
return;
} else {
this.setState({
currentTime: 0,
stoppedRecording: true,
finished: true,
recording: false,
paused: false,
duration: this.sound.getDuration()
});
}
});
return filePath;
} catch (error) {
console.error(error);
}
}
/**
* 用戶隨意退出錄音時處理函數(shù)
* @returns {Promise<void>}
* @private
*/
async _quitRandom() {
if (!this.state.recording) {
console.warn('Can\'t stop, not recording!');
return;
}
try {
const filePath = await AudioRecorder.stopRecording();
return filePath;
} catch (error) {
console.error(error);
}
}
/**
* 播放錄音
* @returns {Promise<void>}
* @private
*/
async _play() {
if (this.state.recording) {
await this._stop();
}
if (!this.state.stoppedRecording) {
ToastAndroid.show("無錄音", ToastAndroid.SHORT);
return;
}
var flag = await FileUtil.existsFile(this.state.audioPath);
if (!flag) {
ToastAndroid.show("無錄音", ToastAndroid.SHORT);
return;
}
this.setState({play: true});
this.sound.play((success) => {
if (success) {
console.log('successfully finished playing');
this.setState({
play: false,
currentTime: 0
})
} else {
console.log('playback failed due to audio decoding errors');
}
});
this.timeout = setInterval(() => {
if (this.sound && this.sound.isLoaded()) {
this.sound.getCurrentTime((seconds, isPlaying) => {
if (!isPlaying) {
clearInterval(this.timeout)
} else {
this.setState({currentTime: seconds});
}
})
}
}, 100);
}
/**
* 開始錄音
* @returns {Promise<void>}
* @private
*/
async _record() {
if (this.state.recording) {
ToastAndroid.show("正在錄音", ToastAndroid.SHORT);
console.warn('Already recording!');
return;
}
if (!this.state.hasPermission) {
ToastAndroid.show("無法錄音植兰,請授予權(quán)限", ToastAndroid.SHORT);
console.warn('Can\'t record, no permission granted!');
return;
}
if (this.state.stoppedRecording) {
this.prepareRecordingPath(this.state.audioPath);
}
this.setState({recording: true, paused: false, play: false});
try {
const filePath = await AudioRecorder.startRecording();
console.warn(filePath);
} catch (error) {
console.error(error);
}
}
_finishRecording(didSucceed, filePath, fileSize) {
this.setState({finished: didSucceed});
console.log(`Finished recording of duration ${this.state.currentTime} seconds at path: ${filePath} and size of ${fileSize || 0} bytes`);
}
/**
* Slider 值變化時對應(yīng)事件
* @param value
*/
onSliderEditing = value => {
if (this.sound) {
this.sound.setCurrentTime(value);
this.setState({currentTime: value});
}
};
/**
* 轉(zhuǎn)換秒的時間格式
* @param seconds
* @returns {string}
*/
getAudioTimeString(seconds) {
const h = parseInt(seconds / (60 * 60));
const m = parseInt(seconds % (60 * 60) / 60);
const s = parseInt(seconds % 60);
return ((h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s));
}
render() {
let _this = this;
const currentTimeString = this.getAudioTimeString(this.state.currentTime);
const durationString = this.getAudioTimeString(this.state.duration);
return (
!_this.state.finished ?
<View style={styles.container}>
<View style={{flex: 1}}>
<View style={styles.svgBackground}>
<Svg height={width} width={width}
viewBox={"0 0 1024 1024"}>{IconLib.IC_SOUND_RECORDING_BACKGROUND}</Svg>
<View style={{position: "absolute"}}>
<Text style={{fontSize: 40}}>{this.getAudioTimeString(_this.state.currentTime)}</Text>
</View>
</View>
<View
style={[styles.bottomContainer, {justifyContent: _this.state.recording ? "space-between" : "center"}]}>
{_this.state.recording ?
<View style={styles.bottomSvg}>
<TouchableOpacity onPress={() => {
this._deleteRandom();
}}>
<Svg height={34} width={34}
viewBox={"0 0 1024 1024"}>{IconLib.IC_SOUND_RECORDING_DELETE}</Svg>
<Text style={styles.bottomText}>刪除</Text>
</TouchableOpacity>
</View> : null}
<View style={styles.bottomSvg}>
{_this.state.recording ?
(_this.state.paused ?
<TouchableOpacity onPress={() => {
_this._resume();
}}>
<Svg height={87} width={87}
viewBox={"0 0 1024 1024"}>{IconLib.IC_SOUND_RECORDING_START}</Svg>
<Text style={styles.bottomText}>點擊繼續(xù)錄音</Text>
</TouchableOpacity> :
<TouchableOpacity onPress={() => {
_this._pause();
}}>
<Svg height={87} width={87}
viewBox={"0 0 1024 1024"}>{IconLib.IC_SOUND_RECORDING}</Svg>
<Text style={styles.bottomText}>點擊暫停錄音</Text>
</TouchableOpacity>)
:
(<TouchableOpacity onPress={() => {
_this._record()
}}>
<Svg height={87} width={87}
viewBox={"0 0 1024 1024"}>{IconLib.IC_SOUND_RECORDING_START}</Svg>
<Text style={styles.bottomText}>點擊開始錄音</Text>
</TouchableOpacity>)}
</View>
{_this.state.recording ?
<View style={styles.bottomSvg}>
<TouchableOpacity onPress={() => {
_this._stop();
}}>
<Svg height={34} width={34}
viewBox={"0 0 1024 1024"}>{IconLib.IC_SOUND_RECORDING_SAVE}</Svg>
<Text style={styles.bottomText}>保存</Text>
</TouchableOpacity>
</View> : null}
</View>
</View>
</View> :
<View style={styles.container}>
<View style={{flex: 1}}>
<View style={styles.svgBackground}>
<Svg height={width} width={width}
viewBox={"0 0 1024 1024"}>{IconLib.IC_SOUND_RECORDING_BACKGROUND}</Svg>
<View style={[styles.playContainer, {display: _this.state.finished ? "flex" : "none"}]}>
<View style={{height: 116, top: 36}}>
<Text style={{fontSize: 15}}>新錄音</Text>
</View>
<View style={{flexDirection: "row"}}>
<Slider style={styles.slider}
onTouchStart={this.onSliderEditStart}
onTouchEnd={this.onSliderEditEnd}
onValueChange={this.onSliderEditing}
value={_this.state.currentTime}
thumbTintColor={"#2683ea"}
minimumTrackTintColor={"#2683ea"}
maximumTrackTintColor={"#dbdbdb"}
maximumValue={_this.state.duration}/>
</View>
<View style={styles.timeContainer}>
<Text style={styles.bottomText}>{currentTimeString}</Text>
<Text style={styles.bottomText}>{durationString}</Text>
</View>
<View style={{height: 40}}/>
<View style={styles.bottomContainer2}>
<View style={styles.bottomSvg}>
<TouchableOpacity onPress={() => {
_this._delete();
}}>
<Svg height={34} width={34}
viewBox={"0 0 1024 1024"}>{IconLib.IC_SOUND_RECORDING_DELETE}</Svg>
<View><Text style={styles.bottomText}>刪除</Text></View>
</TouchableOpacity>
</View>
<View style={styles.bottomSvg}>
{_this.state.play ?
<TouchableOpacity onPress={() => {
_this._pausePlay();
}}>
<Svg height={34} width={34}
viewBox={"0 0 1024 1024"}>{IconLib.IC_SOUND_RECORDING_PLAY}</Svg>
<Text style={styles.bottomText}>暫停</Text>
</TouchableOpacity> :
<TouchableOpacity onPress={() => {
_this._play();
}}>
<Svg height={34} width={34}
viewBox={"0 0 1024 1024"}>{IconLib.IC_SOUND_RECORDING_PAUSE}</Svg>
<Text style={styles.bottomText}>播放</Text>
</TouchableOpacity>
}
</View>
<View style={styles.bottomSvg}>
<TouchableOpacity onPress={() => {
_this.setState({
finished: false,
currentTime: 0,
duration: 0
})
}}>
<Svg height={34} width={34}
viewBox={"0 0 1024 1024"}>{IconLib.IC_SOUND_RECORDING_RERECORD}</Svg>
<Text style={styles.bottomText}>重錄</Text>
</TouchableOpacity>
</View>
</View>
</View>
</View>
<View style={styles.controls}>
<TouchableOpacity style={[styles.bottom, {backgroundColor: "#2683ea"}]}
onPress={() => {
if (this.sound.isLoaded()) {
let audioPath = _this.state.audioPath;
let fileName = audioPath.substring(audioPath.lastIndexOf("/") + 1);
let data = {
fileName: fileName,
path: audioPath,
type: fileName.substring(fileName.lastIndexOf(".") + 1)
};
_this.props.navigation.state.params.callBack(data);
}
_this.props.navigation.goBack();
}}>
<Text style={{fontSize: 16, color: "#ffffff"}}>確認(rèn)</Text>
</TouchableOpacity>
<View style={{height: 16}}/>
<TouchableOpacity style={[styles.bottom, {backgroundColor: "#e2effc"}]}
onPress={() => _this.props.navigation.goBack()}>
<Text style={{fontSize: 16, color: "#2683ea"}}>取消</Text>
</TouchableOpacity>
</View>
</View>
</View>
);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
if (this.timeout) {
clearInterval(this.timeout);
}
AudioRecorder.removeListeners()
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
flexDirection: "column",
},
svgBackground: {
height: width,
backgroundColor: "#d0d0d0",
justifyContent: "center",
alignItems: "center"
},
bottomContainer: {
flex: 1,
paddingHorizontal: 50,
width: width,
backgroundColor: "#fff",
flexDirection: "row",
alignItems: "center"
},
bottomContainer2: {
paddingHorizontal: 50,
justifyContent: "space-between",
alignItems: "center",
flexDirection: "row"
},
bottomSvg: {justifyContent: "center", alignItems: "center"},
bottomText: {fontSize: 15, color: "#606060"},
playContainer: {
width: width - 32,
height: width,
position: "absolute"
},
slider: {
flex: 1,
alignSelf: 'center',
marginHorizontal: Platform.select({ios: 5})
},
timeContainer: {
paddingHorizontal: 10,
justifyContent: "space-between",
alignItems: "center",
flexDirection: "row"
},
controls: {
flex: 1,
paddingHorizontal: 14,
width: width,
backgroundColor: "#fff",
justifyContent: "center",
alignItems: "center"
},
bottom: {
width: width - 28,
height: 45,
borderRadius: 10,
justifyContent: "center",
alignItems: "center"
}
});
export default connect((state) => ({}), (dispatch) => ({}))(SoundRecording)
再來一波效果圖
苦于不能上傳視頻份帐,只好用圖片代替了(剛開始用簡書,不太清楚怎么添加視頻楣导,可惜我錄好的視頻了)
要點
1.防止用戶隨時退出錄音時的處理
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
}
2.錄音中用戶刪除錄音的處理
相關(guān)處理請自行查找