React Native 实践系列:《React-Native-Orientaion 屏幕翻转实战》

React Native 实践系列:《React-Native-Orientaion 屏幕翻转实战》

前言

之前利用 React-Native-Orientaion 完成屏幕反转实战,也踩了一些坑在这里分享一下。

安装和集成

github 所在地址: react-native-orientation

安装命令

npm install react-native-orientation --save


缺陷

只能监听屏幕设备的反转,不能细颗粒到组件层面的翻转,如果需要做到组件层面的翻转,推荐使用 github.com/wonday/react

做兼容

在使用中发现不少坑,比如我在Android 端使用以下办法无效,

Orientation.addSpecificOrientationListener

注意:在iOS 端如果启用方向传感器的话,回调是不会被调用的,而Android 是一直都调用,所以Android的监听我不用这个。

所以,我使用

Dimensions.addEventListener("change",cb) // Android 端监听设备翻转

但这个方法也不是高性能的办法, 根据官方的说法,不仅仅手动翻转设备时会触发,

而且在当前应用的屏幕尺寸发生变化时,比如应用从全屏模式切换到非全屏模式也会触发,还有用户在设置中修改了设备的显示模式也会触发,所以不是很高效。


iOS端在 反复旋转之后偶尔会故障

需要手动设置支持的方向

[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"];
[UIViewController attemptRotationToDeviceOrientation];


翻转之后需要Android解锁

从api的介绍来看,关于lockToxx 中 lock的理解,在iOS和 Android 是不一样的,

iOS是 旋转 ,而Android 是 锁定, 这就意味着如果开启自动旋转时,旋转一个方向成功后,还需要解锁传感器,不然无法发生自动旋转。

所以在Android lockToxx 之后,需要根据读取Android 的系统变量,

android.provider.Settings.System.ACCELEROMETER_ROTATION

完整的获取Android 用户是否自动旋转标志 的方法应该如下:

// 这是我在 react-native-orientation 后续自己添加的方法
@ReactMethod
public void getAutoRotateState(Callback callback) {
     final ContentResolver resolver = getReactApplicationContext().getContentResolver();
     boolean rotateLock = android.provider.Settings.System.getInt(
                resolver,
                android.provider.Settings.System.ACCELEROMETER_ROTATION, 0) == 1;
     callback.invoke(rotateLock);
}


Orientation.getAutoRotateState(autoRotate => {
     if (autoRotate === false) {
        // fix: 锁住时不发生自动旋转
        Orientation.lockToPortrait();
     if(autoRotate){
        // 是否支持自动旋转,若是,则调用unlockAllOrientations (针对Android)
        Orientation.unlockAllOrientations();


对于API的理解

addSpecificOrientationListener

addSpecificOrientationListener((specificOrientation) => {});

为了提升性能还可以 添加防抖措施

addSpecificOrientationListener(debounce(_direction => {},500));


addSpecificOrientationListener 是iOS端监听屏幕翻转的核心方法,

为什么不使用 addOrientationListener 方法,因为它并不能区分是 左水平 还是 右水平,而 addSpecificOrientationListener 可以。

  • LANDSCAPE-LEFT
  • LANDSCAPE-RIGHT
  • PORTRAIT
  • PORTRAITUPSIDEDOWN
  • UNKNOWN

lockToPortrait(或者lockToxxx)

旋转(锁定)某个位置,此外还有 lockToLandScape

在项目中还需要配合 LANDSCAPE-LEFT 和 LANDSCAPE-RIGHT 。


Android 端在开启传感器时反复旋转

unlockAllOrientations 是开启传感器时,可以自动开启旋转屏幕。

反复横跳,有的时候并不能够旋转到指定的位置,所以需要用Android原生监听来规定

,细分什么情况下需要旋转。

private final void startOrientationChangeListener() {
        if (mOrientationListener == null) {
            mOrientationListener = new OrientationEventListener(context) {
                @Override
                public void onOrientationChanged(int rotation) {
                    Log.d("onOrientationChanged", rotation+""+island);
                    int triggerAngle = 45;
                    // 超过45度旋转 ,包括 左边 或者 右边
                    if (((rotation > triggerAngle) &&
                            (rotation < 180 - triggerAngle)) ||
                            ((rotation > 180 + triggerAngle) && (rotation < 360 - triggerAngle))) {//landscape
                        if (!island) {
                            island = true;
                            // 方向传感器是否打开
                            if (isScreenchange(context)) {
                                final Activity activity = getCurrentActivity();
                                if (activity == null) {
                                    return;
                                int orientation = activity.getRequestedOrientation();
                                if (orientation != ActivityInfo.SCREEN_ORIENTATION_SENSOR) {
                                    // 切换为传感器的方向
                                    Log.d("unlockAllOrientations", rotation+"");
                                    unlockAllOrientations();
                    } else {
                        island = false;
        mOrientationListener.enable();
    }


但是记得在页面onPagePause 时消除掉这个 listener

// OrientationModule.java
private final void closeOrientationChangeListener() {
 if (mOrientationListener != null) {
            mOrientationListener.disable();
}


// .js
const pagePause = ()=>{
  if (Platform.OS === "android") {