react-native-video 有全屏播放的方法 presentFullscreenPlayer曙博,但是使用的時候發(fā)現(xiàn)在安卓上全屏方法不好使,雖然可以人為的控制播放窗口大小為全屏怜瞒,視頻是能播放了父泳,但發(fā)現(xiàn)沒有可以控制播放的組件(暫停/播放按鈕,進(jìn)度條等)吴汪。仔細(xì)看文檔發(fā)現(xiàn)有這段說明:
Put the player in fullscreen mode.
On iOS, this displays the video in a fullscreen view controller with controls.
On Android ExoPlayer & MediaPlayer, this puts the navigation controls in fullscreen mode. It is not a complete fullscreen implementation, so you will still need to apply a style that makes the width and height match your screen dimensions to get a fullscreen video.
嘗試了好久還是搞不定惠窄,索性就自己定制播放的控制組件。
為了更好的體驗漾橙,我決定把播放器做成一個單獨全屏播放頁面杆融,當(dāng)點擊播放視頻的時候,直接跳轉(zhuǎn)到播放器頁面霜运,頁面上有關(guān)閉按鈕脾歇,而且安卓上播放頁面點擊回退可以返回剛剛的頁面,比較符合邏輯淘捡。之前我把跳轉(zhuǎn)播放前的頁面和播放組件放同一個頁面藕各,點擊播放時,設(shè)置播放組件全屏可見并把其他內(nèi)容藏起來焦除。但是這樣做有些復(fù)雜激况,而且很難隱藏頁面的狀態(tài)欄和標(biāo)題欄,點擊返回時會回退到上一個頁面膘魄,可能用戶此時只是想退出播放乌逐,回到播放前的頁面。
播放和暫停功能比較容易创葡,圖標(biāo)可以使用 react-native-vector-icons 上的圖標(biāo)浙踢。因為項目使用的是 antd-mobile-rn 的 UI 庫,所以播放進(jìn)度條可以直接使用現(xiàn)成的組件 Slider蹈丸,拖動進(jìn)度條時根據(jù)進(jìn)度條位置重新設(shè)置播放進(jìn)度可以簡單的實現(xiàn)播放進(jìn)度控制(this.player.seek(value))成黄,這里用 this.state.paused 控制視頻的播放和暫停。
關(guān)于橫屏播放逻杖,項目暫時沒有這個需求,為節(jié)省時間思瘟,這里也不深究荸百,可以參考一下這里。最后展示一下效果
詳細(xì)代碼如下:
import * as React from "react";
import {
View,
Text,
StatusBar,
SafeAreaView,
Platform,
TouchableOpacity,
ViewStyle,
TextStyle,
StyleSheet
} from "react-native";
import { Toast, ActivityIndicator, Slider } from "antd-mobile-rn";
import MaterialsIcon from "react-native-vector-icons/MaterialIcons";
import Video from "react-native-video";
import * as _ from "lodash";
import { size, color } from "../../config";
export default class VideoPlayer extends React.Component<any, any> {
static navigationOptions = ({ navigation }: any) => ({
header: null
});
player: any;
constructor(props: IVideoPlayerProps) {
super(props);
this.state = {
onLoading: false,
videoHeight: size.height,
playerIconVisible: true,
paused: true,
playableDuration: 0,
currentTime: 0,
progressPercent: 0
};
}
render() {
// videoSource: { uri: 'http://cdn.xxx.com/assets/videos/xxx.mp4' } OR require('../../local.mp4')
const videoSource = _.get(this.props.navigation, "state.params.source");
let sourceType = _.get(
this.props.navigation,
"state.params.sourceType",
"url"
);
if (!videoSource || (sourceType === "url" && !videoSource.uri)) {
Toast.fail("視頻路徑不正確滨攻!", 2);
return <SafeAreaView style={indicatorStyles.container} />;
}
return (
<SafeAreaView style={indicatorStyles.container}>
<Video
style={{ width: size.width, height: this.state.videoHeight }}
source={videoSource}
ref={(ref: any) => (this.player = ref)}
paused={this.state.paused}
onLoadStart={(e: any) => {
console.log("onLoadStart: ", e);
this.setState({ onLoading: true });
}}
onEnd={() =>
this.setState({ playerIconVisible: true, onLoading: false })
}
onError={() => {
Toast.fail("加載視頻出錯", 2);
this.setState({ onLoading: false });
}}
onLoad={(e: any) => {
console.log("onLoad && ready to play: ", e);
this.setState({ paused: true, onLoading: false });
const height = _.get(e, "naturalSize.height");
const width = _.get(e, "naturalSize.width");
console.log("video' width && height: ", width, height);
let videoHeight = this.state.videoHeight;
if (_.isNumber(height) && _.isNumber(width)) {
videoHeight = (size.width * height) / width;
}
this.setState({
playableDuration: _.get(e, "duration", 0),
videoHeight: videoHeight,
paused: false
});
}}
onProgress={(e: any) => {
// console.log('onProgress: ', e);
let currentTime = _.get(e, "currentTime", 0);
let playableDuration = _.get(e, "playableDuration", 1);
this.setState({
currentTime,
progressPercent: (currentTime / playableDuration) * 100
});
}}
onFullscreenPlayerDidPresent={() => {
this.setState({ paused: false });
}}
onFullscreenPlayerWillDismiss={() => {
this.setState({ paused: true });
}}
/>
<TouchableOpacity
style={indicatorStyles.videoCover}
onPress={async () => {
await this.setState((prevState: any) => ({
playerIconVisible: !prevState.playerIconVisible
}));
if (!this.state.paused && this.state.playerIconVisible) {
setTimeout(() => {
this.setState({ playerIconVisible: false });
}, 3000);
}
}}
>
{this.state.onLoading && <ActivityIndicator color="#fff" />}
<TouchableOpacity
style={{
position: "absolute",
top: Platform.OS === "ios" ? 0 : StatusBar.currentHeight,
right: 0,
padding: 10
}}
onPress={() => this.props.navigation.goBack()}
>
<MaterialsIcon
name="close"
size={30}
color={"rgba(255, 255, 255, 0.8)"}
/>
</TouchableOpacity>
{this.state.playerIconVisible && (
<View style={indicatorStyles.playerController}>
<TouchableOpacity
style={indicatorStyles.playerIcon}
onPress={() => {
this.setState((prevState: any) => ({
paused: !prevState.paused
}));
}}
>
<MaterialsIcon
name={this.state.paused ? "play-arrow" : "pause"}
size={30}
color={"rgba(255, 255, 255, 0.8)"}
/>
</TouchableOpacity>
<Text style={indicatorStyles.playerTime}>
{this.state.currentTime}
</Text>
<View style={indicatorStyles.playerProgress}>
<Slider
value={this.state.currentTime}
min={0}
max={this.state.playableDuration}
onChange={(value: number) => {
this.player.seek(value);
this.setState({ paused: false });
}}
onAfterChange={(value: number) =>
console.log("afterChange: ", value)
}
/>
</View>
<Text style={indicatorStyles.playerTime}>
{this.state.playableDuration}
</Text>
</View>
)}
<View />
</TouchableOpacity>
</SafeAreaView>
);
}
}
const PLAYER_ICON_WIDTH = 30;
const PLAYER_TIME = 50;
const PROGRESS_WIDTH = size.width - PLAYER_ICON_WIDTH - PLAYER_TIME * 2 - 10;
const style = {
container: {
flex: 1,
backgroundColor: "#000",
alignItems: "center",
justifyContent: "center"
},
videoCover: {
position: "absolute",
width: "100%",
height: "100%",
backgroundColor: "transparent",
alignItems: "center",
justifyContent: "center"
},
playerController: {
flexDirection: "row",
position: "absolute",
bottom: 5,
left: 0,
width: "100%",
backgroundColor: "rgba(0,0,0,0.3)",
alignItems: "center"
},
playerIcon: {
width: PLAYER_ICON_WIDTH,
height: 30,
marginLeft: 10,
justifyContent: "center",
alignItems: "center"
},
playerProgress: {
justifyContent: "center",
height: 4,
width: PROGRESS_WIDTH,
flex: 1
},
playerTime: {
width: PLAYER_TIME,
height: 30,
color: "#fff",
padding: 5,
textAlign: "center"
}
};