Modal阻止了Android上的所有新闻事件(React Native.66.4)



我有一个组件在iOS中运行良好,在Android上没有模态元素,但由于某种原因,当我在Android中添加模态时,它会覆盖所有按键(例如,你不能点击任何清晰可见的按钮(。

我试过设置z索引,我几乎什么都试过。我不知道此刻该怎么办。

我的VideoPlayer文件:

import React, { useCallback, useEffect, useRef, useState } from 'react';
import { VideoProperties } from 'react-native-video';
import Video from 'react-native-video-controls';
import { Animated, Dimensions, Modal, StyleSheet, Text, View } from 'react-native';
import { Image } from 'react-native-elements';
import { colors } from '../styles/colorPalette';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { useTheme } from '../contexts/ThemeContext';
import { ReactNativeProps } from 'react-native-render-html';
import Orientation from 'react-native-orientation-locker';
import { useNavigation } from '@react-navigation/native';
interface VideoPlayerProps extends VideoProperties {
autoPlay?: boolean
categoryOverlay?: boolean | string
disableSeekSkip?: boolean
ref?: any
}
const VideoPlayer = (props: VideoPlayerProps & ReactNativeProps) => {
const navigation = useNavigation();
const [vidAspectRatio, setVidAspectRatio] = useState(16 / 9)
const [isFullscreen, setIsFullscreen] = useState(false)
const { darkMode, toggleNavBar } = useTheme();
const [error, setError] = useState(null)
const videoRef = useRef<Video>(null);
const progress = useRef<number>(0)
const dimensions = {
height: Dimensions.get('screen').height,
width: Dimensions.get('screen').width
}
const handleEnterFullscreen = async () => {
Orientation.lockToLandscape()
setIsFullscreen(true)
toggleNavBar(false)
}
const handleExitFullscreen = async () => {
Orientation.unlockAllOrientations()
setIsFullscreen(false)
toggleNavBar(true)
}
const styles = StyleSheet.create({
container: {
aspectRatio: vidAspectRatio ? vidAspectRatio : 1.75,
maxHeight: isFullscreen ? dimensions.width : dimensions.height,
alignItems: 'center',
justifyContent: 'center',
},
containerFSProps: {
resizeMode: 'contain',
marginLeft: 'auto',
marginRight: 'auto',
},
controlsImage: {
resizeMode: 'contain',
width: '100%',
},
modalContainer: {
position: 'relative',
flexGrow: 1,
justifyContent: 'center',
backgroundColor: '#000',
resizeMode: 'contain',
zIndex: -1,
},
playIcon: {
color: darkMode ? colors.primary.purple4 : "#fff",
fontSize: 30,
marginHorizontal: 30,
},
playIconContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
paddingHorizontal: 15,
paddingVertical: 7.5,
borderRadius: 10,
zIndex: 10,
},
video: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
},
videoButton: {
height: 60,
width: 60,
},
videoPlayer: {
position: 'absolute',
height: '100%',
width: '100%',
},
videoPoster: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
resizeMode: 'cover',
},
videoWrapper: {
position: 'absolute',
width: '100%',
height: '100%',
},
volumeOverlay: {
position: 'absolute',
top: 0,
right: 0,
},
categoryOverlay: {
paddingHorizontal: 10,
paddingVertical: 5,
position: 'absolute',
color: '#fff',
bottom: 10,
right: 10,
backgroundColor: 'rgba(0,0,0, .75)',
borderRadius: 10,
zIndex: 999,
textTransform: 'uppercase',
},
topControls: {
flexDirection: 'row',
justifyContent: 'flex-end',
position: 'absolute',
top: 0,
right: 0,
zIndex: 1,
}
});

const VideoPlayerElement = useCallback((props: VideoPlayerProps & ReactNativeProps) => {
const [duration, setDuration] = useState(null);
const [lastTouched, setLastTouched] = useState(0)
const [isPlaying, setIsPlaying] = useState(!props.paused || false);
const [isMuted, setIsMuted] = useState(props.muted || false)
const [controlsActive, setControlsActive] = useState(true);
const { categoryOverlay, disableSeekSkip = false, source } = props;
const handleError = (e: any) => {
console.log("ERROR: ", e)
}
const handleSeek = (num: number) => {
if (!videoRef.current || videoRef.current.state.seeking === true || (Date.now() - lastTouched < 250)) {
return
} else {
videoRef.current.player.ref.seek(Math.max(0, Math.min((videoRef.current.state.currentTime + num), videoRef.current.state.duration)))
setLastTouched(Date.now())
}
}
const handleLoad = (res: any) => {
if (progress.current > 0 && !disableSeekSkip && (progress.current != res.currentTime)) {
videoRef.current.player.ref.seek(progress.current, 300)
}
// set height and duration
duration && setDuration(res.duration ?? null);
setVidAspectRatio(res.naturalSize ? (res.naturalSize.width / res.naturalSize.height) : (16 / 9));
}
const handleMute = () => {
if (isMuted) {
videoRef.current.state.muted = false
setIsMuted(false)
} else {
videoRef.current.state.muted = true
setIsMuted(true)
}
}
const handlePause = (res: any) => {
// The logic to handle the pause/play logic
res.playbackRate === 0 ? setIsPlaying(false) : setIsPlaying(true);
}
const handlePlayPausePress = () => {
videoRef.current.state.paused ? videoRef.current.methods.togglePlayPause(true) : videoRef.current.methods.togglePlayPause(false);
}
const handleProgress = (event: any) => {
progress.current = (event.currentTime);
}
const handleSetControlsActive = (active: boolean) => {
setControlsActive(active)
}
const convertTime = (seconds: number) => {
const secsRemaining = Math.floor(seconds % 60);
return `${Math.floor(seconds / 60)}:${secsRemaining < 10 ? '0' + secsRemaining : secsRemaining}`
}
const convertTimeV2 = (secs: number) => {
var hours   = Math.floor(secs / 3600)
var minutes = Math.floor(secs / 60) % 60
var seconds = Math.floor(secs % 60)
return [hours,minutes,seconds]
.map(v => v < 10 ? "0" + v : v)
.filter((v,i) => v !== "00" || i > 0)
.join(":")
}
return (
<Animated.View style={[styles.container, isFullscreen ? styles.containerFSProps : styles.containerProps]}>
<View style={styles.videoWrapper}>
<Video
ref={videoRef}
source={source}
showOnStart
disableBack
disableFullscreen
disablePlayPause
disableSeekbar={disableSeekSkip}
disableTimer={disableSeekSkip}
disableVolume
doubleTapTime={0}
fullscreen={isFullscreen}
muted={isMuted}
paused={videoRef.current?.state.paused || props.paused}
onEnd={() => { setIsPlaying(false)}}
onEnterFullscreen={handleEnterFullscreen}
onExitFullscreen={handleExitFullscreen}
onMute={() => console.log('mute')}
onLoad={handleLoad}
onError={handleError}
onHideControls={() => handleSetControlsActive(false)}
onShowControls={() => handleSetControlsActive(true)}
onPlay={() => setIsPlaying(true)}
onPause={() => setIsPlaying(false)}
onPlaybackRateChange={handlePause}
onProgress={handleProgress}
seekColor="#a146b7"
controlTimeout={3000}
style={{flex: 1, flexGrow: 1, zIndex: 1}}
containerStyle={{flex: 1, flexGrow: 1}}
/>
</View>
{categoryOverlay && progress.current == 1 && 
<View style={styles.categoryOverlay}>
<Text style={{color: "#fff", textTransform: 'uppercase'}}>{(typeof categoryOverlay === 'boolean') && duration ? convertTime(duration) : categoryOverlay}</Text>
</View>
}
{ (progress.current == 1 && !isPlaying) && <View style={styles.videoPoster}><Image style={{width: '100%', height: '100%', resizeMode: 'contain'}} source={{ uri: `https://home.test.com${props.poster}` }} /></View> }
{ (controlsActive || !isPlaying) && 
<>
{ (controlsActive || !isPlaying) && 
<View style={styles.topControls}>
<TouchableOpacity onPress={handleMute}>
<Image containerStyle={{height: 60, width: 60}} source={isMuted ? require('../assets/icons/Miscellaneous/Video_Controls/volume-muted.png') : require('../assets/icons/Miscellaneous/Video_Controls/volume-on.png')} />
</TouchableOpacity>
<TouchableOpacity onPress={isFullscreen ? handleExitFullscreen : handleEnterFullscreen}>
<Image containerStyle={{height: 60, width: 60}} source={isFullscreen ? require('../assets/icons/Miscellaneous/Video_Controls/minimize.png') : require('../assets/icons/Miscellaneous/Video_Controls/fullscreen.png')} />
</TouchableOpacity>
</View>
}
<View style={styles.playIconContainer}>
{ !disableSeekSkip && <TouchableOpacity disabled={(videoRef?.current?.state?.currentTime == 0) || videoRef?.current?.state?.seeking} onPress={() => handleSeek(-15)}>
<Image containerStyle={{height: 60, width: 60}} style={styles.controlsImage} source={require('../assets/icons/Miscellaneous/Video_Controls/back-15s.png')}/>
</TouchableOpacity> }
<TouchableOpacity onPress={handlePlayPausePress}>
<Image containerStyle={{height: 60, width: 60}} source={isPlaying ? require('../assets/icons/Miscellaneous/Video_Controls/pause-video-white.png') : require('../assets/icons/Miscellaneous/Video_Controls/play-video-white.png')}/>
</TouchableOpacity>
{ !disableSeekSkip && <TouchableOpacity disabled={videoRef?.current?.state?.currentTime == videoRef?.current?.state?.duration || videoRef?.current?.state?.seeking} onPress={() => handleSeek(15)}>
<Image containerStyle={{height: 60, width: 60}} style={styles.controlsImage} source={require('../assets/icons/Miscellaneous/Video_Controls/skip-15s.png')}/>
</TouchableOpacity> }
</View> 
</>}
</Animated.View>
);
}, [isFullscreen])
useEffect(() => {
Orientation.lockToPortrait()
return () => {
Orientation.unlockAllOrientations()
toggleNavBar(true)
}
}, [])
useEffect(() => {
if (error) console.log("ERROR", error)
}, [error])
useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
videoRef.current.methods.togglePlayPause(false);
});
return unsubscribe;
}, [navigation]);

return (
isFullscreen ? 
<Modal hardwareAccelerated animationType='slide' visible={isFullscreen} supportedOrientations={['landscape']}>
<View style={[styles.modalContainer]} >
<VideoPlayerElement {...props} />
</View>
</Modal> 
:
<VideoPlayerElement {...props} />
)
}
export default React.memo(VideoPlayer)

我的Package.json文件(如果需要的话(

{
"name": "app",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
},
"dependencies": {
"@react-navigation/drawer": "^6.1.8",
"@react-navigation/material-top-tabs": "^6.0.6",
"@react-navigation/native": "^6.0.6",
"@react-navigation/native-stack": "^6.2.5",
"html-entities": "^2.3.2",
"react": "17.0.2",
"react-native": "0.66.4",
"react-native-elements": "^3.4.2",
"react-native-gesture-handler": "^1.10.3",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-linear-gradient": "^2.5.6",
"react-native-orientation-locker": "^1.4.0",
"react-native-pager": "^0.0.3",
"react-native-pager-view": "^5.4.9",
"react-native-reanimated": "^2.3.0",
"react-native-render-html": "^6.3.1",
"react-native-safe-area-context": "^3.3.2",
"react-native-screens": "^3.9.0",
"react-native-svg": "^12.1.1",
"react-native-svg-transformer": "^0.14.3",
"react-native-tab-view": "^3.1.1",
"react-native-vector-icons": "^9.0.0",
"react-native-video": "^5.2.0",
"react-native-video-controls": "^2.8.1",
"react-native-webview": "^11.15.0",
"view": "^1.1.1"
},
"devDependencies": {
"@babel/core": "^7.12.9",
"@babel/runtime": "^7.12.5",
"@react-native-community/eslint-config": "^2.0.0",
"@types/jest": "^26.0.23",
"@types/react-native": "^0.65.0",
"@types/react-native-video": "^5.0.10",
"@types/react-test-renderer": "^17.0.1",
"babel-jest": "^26.6.3",
"eslint": "^7.14.0",
"jest": "^26.6.3",
"metro-react-native-babel-preset": "^0.66.2",
"react-test-renderer": "17.0.2",
"typescript": "^4.1.2"
},
"resolutions": {
"@types/react": "^17"
},
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
}
}

从React原生手势处理程序的React原生库内部导入TouchableOpacity

import { TouchableOpacity } from 'react-native';

最新更新