mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-01 22:41:28 +08:00
Listen to device orientation changes
Summary: Similar to iOS, send device orientation changes events. This does not have the `getCurrentOrientation` method, because it's not used. If necessary, we'll add it separately. This also adds a simple example for testing. We listen to orientation changes in `onGlobalLayout`, and check if the rotation of the device has changed. If it has, we emit the event. But: - `onGlobalLayout` (and `onConfigurationChanged` - which is the method usually used for checking for device orientation changes) is *not* called when the device goes from landscape to reverse landscape (same with portrait), as that is not a relayout / configuration change. We could detect if this happens with the help of an `OrientationEventListener`. However, this listener notifies you if the degree of the phone changes by a single degree, which means that you need to know by how many degrees the phone needs to change in order for the orientation to change. I haven't looked into how accurate this could be, but I suspect that in practice it would cause a lot of bugs. A simple `abgs` and google search reveals that everybody uses a different margin for detecting a rotation change (from 30 to 45 degrees), so I suspect that this won't work as expected in practice. Therefore, we're not using this here, and we're sticking to what android provides via `onConfigurationChanged`. If we find that we have issues because users need to know when the user goes from landscape to reverse landscape, then we'll have to revisit this. Reviewed By: foghina Differential Revision: D3797521 fbshipit-source-id: 62508efd342a9a4b41b42b6138c73553cfdefebc
This commit is contained in:
committed by
Facebook Github Bot 4
parent
5d240a8ed3
commit
f07ca31303
@@ -11,14 +11,17 @@ package com.facebook.react;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
@@ -30,10 +33,10 @@ import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
||||
import com.facebook.react.uimanager.JSTouchDispatcher;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.RootView;
|
||||
import com.facebook.react.uimanager.SizeMonitoringFrameLayout;
|
||||
import com.facebook.react.uimanager.JSTouchDispatcher;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||
|
||||
@@ -56,7 +59,7 @@ public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
|
||||
private @Nullable ReactInstanceManager mReactInstanceManager;
|
||||
private @Nullable String mJSModuleName;
|
||||
private @Nullable Bundle mLaunchOptions;
|
||||
private @Nullable KeyboardListener mKeyboardListener;
|
||||
private @Nullable CustomGlobalLayoutListener mCustomGlobalLayoutListener;
|
||||
private @Nullable OnGenericMotionListener mOnGenericMotionListener;
|
||||
private int mRootViewTag;
|
||||
private boolean mWasMeasured = false;
|
||||
@@ -171,7 +174,7 @@ public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
if (mIsAttachedToInstance) {
|
||||
getViewTreeObserver().addOnGlobalLayoutListener(getKeyboardListener());
|
||||
getViewTreeObserver().addOnGlobalLayoutListener(getCustomGlobalLayoutListener());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +182,7 @@ public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
if (mIsAttachedToInstance) {
|
||||
getViewTreeObserver().removeOnGlobalLayoutListener(getKeyboardListener());
|
||||
getViewTreeObserver().removeOnGlobalLayoutListener(getCustomGlobalLayoutListener());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,11 +258,11 @@ public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
|
||||
mWasMeasured = true;
|
||||
}
|
||||
|
||||
private KeyboardListener getKeyboardListener() {
|
||||
if (mKeyboardListener == null) {
|
||||
mKeyboardListener = new KeyboardListener();
|
||||
private CustomGlobalLayoutListener getCustomGlobalLayoutListener() {
|
||||
if (mCustomGlobalLayoutListener == null) {
|
||||
mCustomGlobalLayoutListener = new CustomGlobalLayoutListener();
|
||||
}
|
||||
return mKeyboardListener;
|
||||
return mCustomGlobalLayoutListener;
|
||||
}
|
||||
|
||||
private void attachToReactInstanceManager() {
|
||||
@@ -269,7 +272,7 @@ public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
|
||||
|
||||
mIsAttachedToInstance = true;
|
||||
Assertions.assertNotNull(mReactInstanceManager).attachMeasuredRootView(this);
|
||||
getViewTreeObserver().addOnGlobalLayoutListener(getKeyboardListener());
|
||||
getViewTreeObserver().addOnGlobalLayoutListener(getCustomGlobalLayoutListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -291,13 +294,14 @@ public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
|
||||
mRootViewTag = rootViewTag;
|
||||
}
|
||||
|
||||
private class KeyboardListener implements ViewTreeObserver.OnGlobalLayoutListener {
|
||||
private class CustomGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
|
||||
private final Rect mVisibleViewArea;
|
||||
private final int mMinKeyboardHeightDetected;
|
||||
|
||||
private int mKeyboardHeight = 0;
|
||||
private int mDeviceRotation = 0;
|
||||
|
||||
/* package */ KeyboardListener() {
|
||||
/* package */ CustomGlobalLayoutListener() {
|
||||
mVisibleViewArea = new Rect();
|
||||
mMinKeyboardHeightDetected = (int) PixelUtil.toPixelFromDIP(60);
|
||||
}
|
||||
@@ -305,16 +309,17 @@ public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
if (mReactInstanceManager == null || !mIsAttachedToInstance ||
|
||||
mReactInstanceManager.getCurrentReactContext() == null) {
|
||||
FLog.w(
|
||||
ReactConstants.TAG,
|
||||
"Unable to dispatch keyboard events in JS as the react instance has not been attached");
|
||||
mReactInstanceManager.getCurrentReactContext() == null) {
|
||||
return;
|
||||
}
|
||||
checkForKeyboardEvents();
|
||||
checkForDeviceOrientationChanges();
|
||||
}
|
||||
|
||||
private void checkForKeyboardEvents() {
|
||||
getRootView().getWindowVisibleDisplayFrame(mVisibleViewArea);
|
||||
final int heightDiff =
|
||||
DisplayMetricsHolder.getWindowDisplayMetrics().heightPixels - mVisibleViewArea.bottom;
|
||||
DisplayMetricsHolder.getWindowDisplayMetrics().heightPixels - mVisibleViewArea.bottom;
|
||||
if (mKeyboardHeight != heightDiff && heightDiff > mMinKeyboardHeightDetected) {
|
||||
// keyboard is now showing, or the keyboard height has changed
|
||||
mKeyboardHeight = heightDiff;
|
||||
@@ -333,6 +338,52 @@ public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForDeviceOrientationChanges() {
|
||||
final int rotation =
|
||||
((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
|
||||
.getDefaultDisplay().getRotation();
|
||||
if (mDeviceRotation == rotation) {
|
||||
return;
|
||||
}
|
||||
mDeviceRotation = rotation;
|
||||
emitOrientationChanged(rotation);
|
||||
}
|
||||
|
||||
private void emitOrientationChanged(final int newRotation) {
|
||||
String name;
|
||||
double rotationDegrees;
|
||||
boolean isLandscape = false;
|
||||
|
||||
switch (newRotation) {
|
||||
case Surface.ROTATION_0:
|
||||
name = "portrait-primary";
|
||||
rotationDegrees = 0.0;
|
||||
break;
|
||||
case Surface.ROTATION_90:
|
||||
name = "landscape-primary";
|
||||
rotationDegrees = -90.0;
|
||||
isLandscape = true;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
name = "portrait-secondary";
|
||||
rotationDegrees = 180.0;
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
name = "landscape-secondary";
|
||||
rotationDegrees = 90.0;
|
||||
isLandscape = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
WritableMap map = Arguments.createMap();
|
||||
map.putString("name", name);
|
||||
map.putDouble("rotationDegrees", rotationDegrees);
|
||||
map.putBoolean("isLandscape", isLandscape);
|
||||
|
||||
sendEvent("namedOrientationDidChange", map);
|
||||
}
|
||||
|
||||
private void sendEvent(String eventName, @Nullable WritableMap params) {
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.getCurrentReactContext()
|
||||
|
||||
Reference in New Issue
Block a user