React-Native之播放器全屏播放
前言
我们要在react-native中实现一个音/视频的播放器,首先就要使用大名鼎鼎的react-native-video库,该库在github上有5.3k的star,很多的播放器都由使用该库封装而成,我们也不例外,使用这个库来封装出我们自己的播放器。
效果
老规矩先上效果图:
Android直屏:
iOS刘海屏:
分析
我们想要的效果是无论是Android还是iOS,无论是普通直屏还是刘海屏等异形屏,都有相同的用户体验。其中最重要的,就是全屏状态下对不同系统、屏幕的适配。
- 普通直屏在全屏状态下,全屏展示
- 刘海屏在全屏状态下,避开头部的刘海区域进行全屏展示
- 根据视频内容的高宽度来确定全屏播放是横屏还是竖屏
难点分析
-
目前react-native并没有方法可以明确区分普通直屏和刘海屏,所以需要我们自己计算高度来进行相关的探索;
-
同样是刘海屏,Android和iOS的表现并不一致,在Android中刘海区域显示状态栏并占据高度,而iOS1刘海区域并不占据高度,内容可以显示在刘海区域与状态栏叠加
技术方案
经过多个机型的高度计算实验,我们分析屏幕高度的计算方式如下图所示:

对于普通直屏
- 绿色区域+红色区域+蓝色区域 =
Dimensions.get('screen').height
- 绿色区域 = 状态栏高度
- 红色区域 = 内容区域高度 =
Dimensions.get('window').height
- 状态栏高度 - 蓝色区域 = 虚拟操作栏高度 =
Dimensions.get('screen').height
-Dimensions.get('window').height
- 全屏高度 = 状态栏高度+内容区域高度 =
Dimensions.get('window').height

对于Android刘海屏
- 绿色区域+红色区域+蓝色区域 =
Dimensions.get('screen').height
- 绿色区域 = 状态栏高度
- 红色区域 = 内容区域高度 =
Dimensions.get('window').height
- 蓝色区域 = 虚拟操作栏高度 =
Dimensions.get('screen').height
-Dimensions.get('window').height
-状态栏高度 - 全屏高度 = 内容区域高度+虚拟操作栏高度 =
Dimensions.get('screen').height
- 状态栏高度

对于IOS刘海屏
- 绿色区域+红色区域 =
Dimensions.get('screen').height
=Dimensions.get('window').height
- 绿色区域 = 状态栏高度+一小部分间隔高度 = 44
- 红色区域 = 内容区域高度 =
Dimensions.get('window').height
- 绿色区域高度 - 全屏高度 = 内容区域高度
以上是各种屏幕高度的计算方式。知道了不同屏幕全屏的高度计算方式,我们只要能区分直屏和刘海屏就可以实现全屏播放啦。
这里是我反复计算高度摸索出来的办法:
区分直屏还是刘海屏
使用view作为最底层容器,给它设置一个"flex:1"的样式,使其充满屏幕,在view的onlayout方法中获取它的宽高,这个高宽作为内容区域的高宽,判断Dimensions.get('window').height
高度大于内容区域高度,且
Dimensions.get('window').height
- 内容区域高度大于等于状态栏高度(在android手机计算中,高度总是反复变化,并不会准确的等于状态栏高度),则该手机是Android直屏或ios刘海屏,再判断系统类型来具体区分是什么手机;否则是android刘海屏。
示例代码如下:
//获取内容区域高宽
/// 屏幕旋转时宽高会发生变化,可以在onLayout的方法中做处理,比监听屏幕旋转更加及时获取宽高变化
_onLayout = (event) => {
//获取根View的宽高
let {width, height} = event.nativeEvent.layout;
console.log('通过onLayout得到的宽度:' + width);
console.log('通过onLayout得到的高度:' + height);
// 一般设备横屏下都是宽大于高,这里可以用这个来判断横竖屏
let isLandscape = (width > height);
if (isLandscape) {
this.setState({
screenLongSize: width,
screenShortSize: height,
})
} else {
this.setState({
screenLongSize: height,
screenShortSize: width,
})
}
}
import {Dimensions} from 'react-native';
import {getStatusBarHeight} from 'react-native-iphone-x-helper';
const screenWidth = Dimensions.get('window').width;
const screenHeight = Dimensions.get('window').height;
const statusBarHeight = getStatusBarHeight();
const allWidth = Dimensions.get('screen').width;
const allHeight = Dimensions.get('screen').height;
//代表是普通直屏,屏幕顶部没有刘海、水滴等形状
if (screenHeight > screenLongSize && parseInt(String(screenHeight -screenLongSize))>= parseInt(String(statusBarHeight))) {
if (Platform.OS === 'ios' && isIphoneX()){
console.log("是ios刘海屏哦");
this.setState({
videoWidth: screenHeight - statusBarHeight - 15,
videoHeight: screenWidth,
})
}else{
console.log("是直屏哦");
//Android直屏和iOS直屏设置相同
this.setState({
videoWidth: screenHeight,
videoHeight: screenWidth,
})
}
} else {//代表是Android刘海屏等异形屏,因为刘海所占的空间一直存在,所以不需要加上状态栏高度
console.log("是Android异形屏哦");
this.setState({
videoWidth: allHeight - statusBarHeight,
videoHeight: screenWidth,
})
}
判断视频全屏需要横屏还是竖屏
需要在Video组件加载视频资源时(onLoad方法)进行判断,如果获取到视频信息的高>宽,则竖屏显示;如果高<=宽,则横屏显示。这里不建议使用data中的orientation属性进行判断,有时高>宽,orientation值也为“landscape”,并不准确。
_onLoaded = (data) => {
console.log('视频加载完成');
console.log("视频信息==============", data);
//如果解析视频方向为竖向,则全屏显示为竖向显示 (默认是横向)
if (data.naturalSize.height > data.naturalSize.width) {
this.setState({
suggestDirection: 'portrait'
})
} else if (data.naturalSize.height <= data.naturalSize.width) {
this.setState({
suggestDirection: 'landscape'
})
}
this.setState({
showLoading: false,
duration: data.duration,
});
};
结尾
以上就是这次做音/视频播放器关于全屏播放的探索与思考,欢迎大家探讨指正~
最后,是一凡的公众号,用来记录学习、工作中的技术思考,大家一起努力!