React Native TouchableOpacity OnPressOut запускается до завершения OnPressIn - PullRequest
0 голосов
/ 11 июля 2020

Я новичок в React Native, но у меня проблемы с решением этой проблемы.

Желаемое поведение компонента Recorder - начинать запись звука при нажатии TouchableOpacity, и продолжайте запись, пока она не будет отпущена, после чего запись остановится. В основном цель состоит в том, чтобы заставить его вести себя как запись видео Snapchat.

Однако у меня возникают проблемы, когда запись очень короткая. Для создания экземпляра / начала записи звука требуется некоторое время, поэтому, если кнопка будет отпущена до того, как это произойдет, сработает функция OnPressOut. Проблема в том, что этот OnPressOut прервет OnPressIn, который выбрасывает все из строя.

По сути, мне интересно, есть ли способ гарантировать, что OnPressIn завершится до OnPressOut запускается. Как я уже сказал, полный RN noob, так что извините, если это очевидное исправление. Я напортачил с InteractionManager, но независимо от того, что это за поведение сохранилось. В любом случае, вот компонент и журнал быстрого нажатия, как я описывал, спасибо!

import React, {Component} from 'react';
import {View, TouchableOpacity, Text, ActivityIndicator, InteractionManager} from 'react-native';
import { Audio } from 'expo-av';
import * as Permissions from 'expo-permissions';
import * as FileSystem from 'expo-file-system';
import styles from './styles.js'

class Recorder extends Component {

    constructor(props) {
      super(props);
      this.state = { recording: new Audio.Recording(),
                     isRecording: false };
      this.recordingSettings = JSON.parse(JSON.stringify(Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY));
    }
  
    async stopRecording(){
      console.log('STOP RECORDING ENTERED')
      try {
        this.setState({isRecording: false});
        await Audio.setAudioModeAsync({
          allowsRecordingIOS: true,
          interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
          playsInSilentModeIOS: true,
          shouldDuckAndroid: true,
          interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
          playThroughEarpieceAndroid: false,
          staysActiveInBackground: true,
        })
        await this.state.recording.stopAndUnloadAsync();
        const info = await FileSystem.getInfoAsync(this.state.recording.getURI());
        this.props.handlePress(info.uri.replace('file://',''))
      } catch (error) {
        if (this.state.recording != null) {
          await this.state.recording.stopAndUnloadAsync();
          this.setState({recording: null, isRecording: false})
        }
        console.log(error)
      } finally{
        console.log('STOP RECORDING EXITED')
      }
    }

    async startRecording(){
      console.log('START RECORDING ENTERED')
      try {
        this.setState({isRecording: true});
        resp = await Audio.setAudioModeAsync({
          allowsRecordingIOS: true,
          interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
          playsInSilentModeIOS: true,
          shouldDuckAndroid: true,
          interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
          playThroughEarpieceAndroid: false,
          staysActiveInBackground: true,
        })
        this.state.recording.setOnRecordingStatusUpdate(null);
        this.setState({recording: null})

        var recording = new Audio.Recording();
        await recording.prepareToRecordAsync();
        recording.setOnRecordingStatusUpdate(this._updateScreenForRecordingStatus);
        this.setState({recording: recording});
        await this.state.recording.startAsync(); 
      } catch (error) {
        if (this.state.recording != null) {
          await this.state.recording.stopAndUnloadAsync();
          recording = null
          this.setState({recording: null, isRecording: false})
        }
        console.log(error)
      } finally{      
        console.log('START RECORDING EXITED')  
      }
    }
  
    async componentDidMount(){
      await Permissions.askAsync(Permissions.AUDIO_RECORDING)
    }
  
    render() {
      if (this.state.isRecording){
        buttonStyle = styles.captureBtnInternal;
        recordingMessage = 'Recording...'
      }
      else if (this.props.isMatching){
        recordingMessage = 'Getting your matches...'
      }
      else if (this.props.isUploading){
        recordingMessage = 'Uploading song...'
      }
      else{
        buttonStyle = styles.captureBtn;
        recordingMessage = 'Press and hold to record'
      }
      return (
          <View style = {styles.alignCenter}>
            <Text style = {styles.buttonText}>{recordingMessage}</Text>
            {this.props.isMatching || this.props.isUploading
            ?
            <ActivityIndicator/>
            :
            <TouchableOpacity 
                    //Looks like if it goes too fast, onPressIn triggers, then onPressOut has to finish before before resuming, maybe ask SO?
                    onPressIn={() => this.startRecording()}
                    onPressOut={() => this.stopRecording()}
                    style={buttonStyle}>
            </TouchableOpacity>
            
          }
          </View>
        
      )
    }
  } 

  
export default Recorder
 LOG  START RECORDING ENTERED
 LOG  STOP RECORDING ENTERED
 LOG  [Error: Prepare encountered an error: recorder not prepared.]
 LOG  START RECORDING EXITED
 LOG  [TypeError: null is not an object (evaluating 'this.state.recording.stopAndUnloadAsync')]
 LOG  STOP RECORDING EXITED
...