React Native RecyclerListView实现视频出屏暂停(类Reels/TikTok)
解决RecyclerListView中React Native Video同时播放的问题
嘿,我之前开发类似TikTok/Reels的短视频应用时也碰到过一模一样的问题!RecyclerListView默认不会帮我们管理子组件的播放状态,所以所有视频一加载就会同时播放。不过别担心,我们可以利用RecyclerListView提供的可见行监听机制来实现仅播放屏幕内视频的功能,具体步骤如下:
核心思路
RecyclerListView有一个onVisibleIndicesChanged回调,它会在列表滚动、可见行变化时触发,告诉我们当前哪些索引的行是可见的。我们可以借助这个回调跟踪当前可见的视频索引,然后把这个状态传递给每个VideoPlayer组件,让它们根据自己是否在可见范围内来自动控制播放/暂停,同时保留用户手动暂停的交互。
步骤1:修改父组件(App.js)
首先,我们需要在父组件中添加状态来存储当前可见的索引,并实现回调函数来更新这个状态,同时把索引信息传递给每个VideoPlayer:
import VideoPlayer from './ViewVideo'; const fakeData = [ { type: 'NORMAL', item: { uri: require('./images/likd2.mp4'), }, }, { type: 'NORMAL', item: { uri: require('./images/Linkd.mp4'), }, }, { type: 'NORMAL', item: { uri: require('./images/PlayDate.mp4'), }, }, ]; export default class Myworld extends React.Component { state = { currentVisibleIndices: [] // 存储当前可见的视频索引 }; dataProvider = new DataProvider((r1, r2) => { return r1 !== r2; }).cloneWithRows(fakeData); layoutProvider = new LayoutProvider( (i) => { return this.dataProvider.getDataForIndex(i).type; }, (type, dim) => { switch (type) { case 'NORMAL': dim.width = '100%'; dim.height = '100%'; break; default: dim.width = '100%'; dim.height = '100%'; break; } }, ); // 处理可见行变化的回调 handleVisibleIndicesChanged = (visibleIndices) => { // 对于全屏滚动的Reels,通常只有一个完全可见的视频,所以取数组内的索引即可 this.setState({ currentVisibleIndices: visibleIndices }); }; rowRenderer = (type, data, index) => { switch (type) { case 'NORMAL': return ( <View> {/* 传递当前视频的索引和可见索引列表给VideoPlayer */} <VideoPlayer source={data.item.uri} index={index} visibleIndices={this.state.currentVisibleIndices} /> </View> ); } }; render() { return ( <> <SafeAreaView style={styles.container}> <RecyclerListView style={styles.videolistcontainer} rowRenderer={this.rowRenderer} dataProvider={this.dataProvider} layoutProvider={this.layoutProvider} initialOffset={1} pagingEnabled={true} showsVerticalScrollIndicator={false} // 添加可见行变化的监听 onVisibleIndicesChanged={this.handleVisibleIndicesChanged} /> </SafeAreaView> </> ); } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', backgroundColor: '#14171A', }, videolistcontainer: { flex: 1, width: '100%' }, // 补充样式,让列表占满屏幕 });
步骤2:修改VideoPlayer组件(ViewVideo.js)
接下来,我们需要在VideoPlayer组件中根据传入的可见索引来自动控制播放状态,同时保留用户手动暂停的功能:
import React, { useState, useEffect, useRef } from 'react'; import { TouchableOpacity, StyleSheet, Dimensions } from 'react-native'; import Video from 'react-native-video'; const { width: WP, height: HP } = Dimensions.get('window'); const VideoPlayer = ({ source, index, visibleIndices }) => { const videoRef = useRef(null); const [userPaused, setUserPaused] = useState(false); // 记录用户手动暂停的状态 // 监听可见索引变化,自动控制播放 useEffect(() => { const isVisible = visibleIndices.includes(index); if (videoRef.current) { if (isVisible && !userPaused) { // 可见且用户未手动暂停,自动播放 videoRef.current.play(); } else { // 不可见或用户手动暂停,暂停视频 videoRef.current.pause(); } } }, [visibleIndices, index, userPaused]); // 组件卸载时暂停视频,避免内存泄漏 useEffect(() => { return () => { if (videoRef.current) { videoRef.current.pause(); } }; }, []); // 处理用户点击播放/暂停 const handleTogglePlay = () => { setUserPaused(!userPaused); if (userPaused) { videoRef.current.play(); } else { videoRef.current.pause(); } }; return ( <TouchableOpacity style={{ height: HP, width: WP }} onPress={handleTogglePlay} > <Video ref={videoRef} source={source} style={styles.backgroundVideo} resizeMode={'cover'} onError={(error) => console.log('Video error:', error)} onLoad={() => console.log('Video loaded')} onProgress={(progress) => console.log('Video progress:', progress)} onEnd={() => { // 视频播放结束后重置用户暂停状态,下次进入可见范围自动播放 setUserPaused(false); }} repeat={false} rate={1.0} volume={1.0} muted={false} /> </TouchableOpacity> ); }; export default VideoPlayer; const styles = StyleSheet.create({ backgroundVideo: { position: 'absolute', width: WP, height: HP, left: 0, right: 0, top: 0, bottom: 0, }, });
关键细节说明
- 可见行监听:通过
onVisibleIndicesChanged获取当前可见的索引,因为我们设置了pagingEnabled={true},所以每次滚动只会有一个视频完全可见,visibleIndices数组里只会有一个索引。 - 状态分离:把用户手动暂停的状态(
userPaused)和自动播放的逻辑分开,这样既实现了自动播放/暂停,又不会覆盖用户的手动操作。 - 内存泄漏处理:在组件卸载时暂停视频,避免视频在后台继续播放占用资源。
- 播放结束处理:视频播放结束后重置
userPaused状态,这样当用户滚动回来时,视频会自动重新播放。
这样修改后,你就可以实现只有屏幕可见的视频才会播放,滚动出屏幕的视频自动暂停的效果啦!
内容的提问来源于stack exchange,提问作者Hari
相关产品推荐
相关产品推荐

