React-Native之播放器全屏播放

644 阅读6分钟

React-Native之播放器全屏播放

前言

我们要在react-native中实现一个音/视频的播放器,首先就要使用大名鼎鼎的react-native-video库,该库在github上有5.3k的star,很多的播放器都由使用该库封装而成,我们也不例外,使用这个库来封装出我们自己的播放器。

效果

老规矩先上效果图:

Android直屏:

iOS刘海屏:

分析

我们想要的效果是无论是Android还是iOS,无论是普通直屏还是刘海屏等异形屏,都有相同的用户体验。其中最重要的,就是全屏状态下对不同系统、屏幕的适配。

  • 普通直屏在全屏状态下,全屏展示
  • 刘海屏在全屏状态下,避开头部的刘海区域进行全屏展示
  • 根据视频内容的高宽度来确定全屏播放是横屏还是竖屏
难点分析
  • 目前react-native并没有方法可以明确区分普通直屏和刘海屏,所以需要我们自己计算高度来进行相关的探索;

  • 同样是刘海屏,Android和iOS的表现并不一致,在Android中刘海区域显示状态栏并占据高度,而iOS1刘海区域并不占据高度,内容可以显示在刘海区域与状态栏叠加

技术方案

经过多个机型的高度计算实验,我们分析屏幕高度的计算方式如下图所示:

Android直屏手机
对于普通直屏
  • 绿色区域+红色区域+蓝色区域 = 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刘海屏手机
对于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 {Dimensionsfrom '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,
        });
};

结尾

以上就是这次做音/视频播放器关于全屏播放的探索与思考,欢迎大家探讨指正~