React Native - 在导航时暂停视频

React Native - Pause Video on Navigation

在我的 react-native 项目中,我将 react-navigation 5 用于导航,将 react-native-video 用于 audio/video 播放器。

我的要求是,当用户导航到另一个屏幕时,如果 audio/video 应该停止播放。然而,这并没有发生,音频继续播放。

我在堆栈导航器中创建了两个屏幕。视频播放器是一个单独的组件。

屏幕代码:

function MainScreen({ navigation }) {
  const [audiostatus, setAudioStatus] = useState(true);

  React.useEffect(() => {
    const unsubscribe = navigation.addListener('blur', () => {
      console.log('Leaving Home Screen');
      setAudioStatus(true);
    });

    return unsubscribe;
  }, [navigation]);

  return (
    <View style={{ flex: 1, justifyContent: 'center',backgroundColor: '#fff' }}>
      <Player tracks={TRACKS} paused={audiostatus} />
      <Button
        title="Go to Screen Without Audio"
        onPress={() => navigation.navigate('No Audio Screen')}
      />
      <Button
        title="Go to Screen With Another Audio (Love Yourself)"
        onPress={() => navigation.navigate('Another Audio Screen')}
      />
    </View>
  );
}

玩家代码 在播放器中,我收到 paused 属性来决定视频是应该已经播放还是暂停。然后播放器通过改变状态来控制播放。

export default class Player extends Component {
  constructor(props) {
    super(props);

    this.state = {
      paused: props.paused,
      totalLength: 1,
      currentPosition: 0,
      selectedTrack: 0,
      repeatOn: false,
      shuffleOn: false,
    };
  }


  setDuration(data) {
    this.setState({totalLength: Math.floor(data.duration)});
  }

  setTime(data) {
    this.setState({currentPosition: Math.floor(data.currentTime)});
  }

  seek(time) {
    time = Math.round(time);
    this.refs.audioElement && this.refs.audioElement.seek(time);
    this.setState({
      currentPosition: time,
      paused: false,
    });
  }

  render() {
    const track = this.props.tracks[this.state.selectedTrack];
    const video = this.state.isChanging ? null : (
      <Video source={{uri: track.audioUrl}} // Can be a URL or a local file.
        ref="audioElement"
        paused={this.state.paused}               // Pauses playback entirely.
        resizeMode="cover"           // Fill the whole screen at aspect ratio.
        repeat={false}                // Repeat forever.
        onLoadStart={this.loadStart} // Callback when video starts to load
        onLoad={this.setDuration.bind(this)}    // Callback when video loads
        onProgress={this.setTime.bind(this)}    // Callback every ~250ms with currentTime
        onEnd={this.onEnd}
        onError={this.videoError}
        style={styles.audioElement}
        audioOnly={true} />
    );

    return (
      <View style={styles.container}>
        <SeekBar
          onSeek={this.seek.bind(this)}
          trackLength={this.state.totalLength}
          onSlidingStart={() => this.setState({paused: true})}
          currentPosition={this.state.currentPosition} />
        <Controls
          onPressPlay={() => this.setState({paused: false})}
          onPressPause={() => this.setState({paused: true})}
          paused={this.state.paused}/>
        {video}
      </View>
    );
  }
}

问题是,一旦用户开始播放视频,然后如果他导航到另一个屏幕,视频就会继续播放。我想让视频暂停。在屏幕上,我添加了 useEffect() 来设置 audiostatus 在屏幕模糊时暂停,但没有任何反应。视频一直在播放。请帮忙

您的 Player 似乎只在 paused 道具挂载时在构造函数中引用一次。 Player 在父组件中更改并在挂载后传递时,不会对 props.paused 的任何更改做出反应或处理。实施 componentDidUpdate 以响应对 props.paused 的更新以更新组件状态。

export default class Player extends Component {
  constructor(props) {
    super(props);

    this.state = {
      paused: props.paused,
      totalLength: 1,
      currentPosition: 0,
      selectedTrack: 0,
      repeatOn: false,
      shuffleOn: false,
    };
  }

  ...

  componentDidUpdate(prevProps, prevState) {
    const { paused } = this.props;
    if (!prevState.paused && paused) {
      this.setState({ paused });
    }
  }

  ...

  render() {
    ...
    const video = this.state.isChanging ? null : (
      <Video
        ...
        paused={this.state.paused}
        ...
      />
    );

    return (
      <View style={styles.container}>
        ...
        {video}
      </View>
    );
  }
}

按照以下方式暂停视频

import React, {useState, useRef} from 'react';

function MainScreen({ navigation }) {
  const [audiostatus, setAudioStatus] = useState(true);

  // create ref
  const playerRef = useRef();

  React.useEffect(() => {
    const unsubscribe = navigation.addListener('blur', () => {
      console.log('Leaving Home Screen');
      setAudioStatus(false); 

      // new code add to pause video from ref
      playerRef.current.pauseVideo();
    });

    return unsubscribe;
  }, [navigation]);

  return (
    <View style={{ flex: 1, justifyContent: 'center',backgroundColor: '#fff' }}>
      <Player ... playerRef={playerRef} />
    </View>
  );
}

像我一样将 Player class 转换为 Hooks

import React, {useState, useImperativeHandle, useRef} from 'react';

function Player = (props) => {

  const [paused, setPaused] = useState(props.paused);
  const [totalLength, setTotalLength] = useState(1);
  const [currentPosition, setCurrentPosition] = useState(0);
  const [selectedTrack, setSelectedTrack] = useState(0);
  const [repeatOn, setRepeatOn] = useState(false);
  const [shuffleOn, setShuffleOn] = useState(false);
  const [isChanging, setIsChanging] = useState(false);

  const audioElement = useRef(null);

  const setDuration = (data) => {
    setTotalLength(Math.floor(data.duration));
  }

  const setTime = (data) => {
    setCurrentPosition(Math.floor(data.currentTime));
  }

  const seek = (time) => {
    time = Math.round(time);
    audioElement && audioElement.current.seek(time);
    setCurrentPosition(time);
    setPaused(false);
  }

  const loadStart = () => {}

  // add for accessing ref
  useImperativeHandle(props.playerRef, () => ({
    pauseVideo: () => setPaused(true),
  }));

    const track = props.tracks[selectedTrack];
    const video = isChanging ? null : (
      <Video source={{uri: track.audioUrl}} // Can be a URL or a local file.
        ref={audioElement}
        paused={paused}               // Pauses playback entirely.
        resizeMode="cover"
        ....
        onLoadStart={loadStart} // new added
        onLoad={setDuration} // new added
      />
    );

    return (
      <View style={styles.container}>
        <SeekBar
          onSeek={seek}
          trackLength={totalLength}
          onSlidingStart={() => setPaused(true)}
          currentPosition={currentPosition} />
        <Controls
          onPressPlay={() => setPaused(false) }
          onPressPause={() => setPaused(true)}
          paused={paused}/>
        {video}
      </View>
   );
}

一个带有功能组件和挂钩的简单解决方案是使用

useIsFocused

which returns true or false 并在更改时重新渲染组件 import it using

import { useIsFocused } from '@react-navigation/native';

 const screenIsFocused = useIsFocused();

如果您使用的是“react-native-video”或任何其他需要类似内容的库

isPaused

你可以使用

 paused={isPaused || (!screenIsFocused )}

视频只有在未暂停且屏幕也处于对焦状态时才会 运行