diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java index aada76ee9..b5939823a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java @@ -482,6 +482,28 @@ public class NativeViewHierarchyManager { outputBuffer[3] = v.getHeight(); } + /** + * Returns the coordinates of a view relative to the entire phone screen (not just the RootView + * which is what measure will return) + * + * @param tag - the tag for the view + * @param outputBuffer - output buffer that contains [x,y,width,height] of the view in coordinates + * relative to the device window + */ + public void measureInWindow(int tag, int[] outputBuffer) { + UiThreadUtil.assertOnUiThread(); + View v = mTagsToViews.get(tag); + if (v == null) { + throw new NoSuchNativeViewException("No native view for " + tag + " currently exists"); + } + + v.getLocationOnScreen(outputBuffer); + + // outputBuffer[0,1] already contain what we want + outputBuffer[2] = v.getWidth(); + outputBuffer[3] = v.getHeight(); + } + public int findTargetTagForTouch(int reactTag, float touchX, float touchY) { View view = mTagsToViews.get(reactTag); if (view == null) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java index 95cd4756e..9e63b313f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java @@ -380,8 +380,8 @@ public class UIImplementation { } /** - * Determines the location on screen, width, and height of the given view and returns the values - * via an async callback. + * Determines the location on screen, width, and height of the given view relative to the root + * view and returns the values via an async callback. */ public void measure(int reactTag, Callback callback) { // This method is called by the implementation of JS touchable interface (see Touchable.js for @@ -391,6 +391,15 @@ public class UIImplementation { mOperationsQueue.enqueueMeasure(reactTag, callback); } + /** + * Determines the location on screen, width, and height of the given view relative to the device + * screen and returns the values via an async callback. This is the absolute position including + * things like the status bar + */ + public void measureInWindow(int reactTag, Callback callback) { + mOperationsQueue.enqueueMeasureInWindow(reactTag, callback); + } + /** * Measures the view specified by tag relative to the given ancestorTag. This means that the * returned x, y are relative to the origin x, y of the ancestor view. Results are stored in the diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index 9742fc041..ddeb94e6d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -253,6 +253,16 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements mUIImplementation.measure(reactTag, callback); } + /** + * Determines the location on screen, width, and height of the given view relative to the device + * screen and returns the values via an async callback. This is the absolute position including + * things like the status bar + */ + @ReactMethod + public void measureInWindow(int reactTag, Callback callback) { + mUIImplementation.measureInWindow(reactTag, callback); + } + /** * Measures the view specified by tag relative to the given ancestorTag. This means that the * returned x, y are relative to the origin x, y of the ancestor view. Results are stored in the diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java index 9b2d81057..b3b931e0f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java @@ -385,6 +385,38 @@ public class UIViewOperationQueue { } } + private final class MeasureInWindowOperation implements UIOperation { + + private final int mReactTag; + private final Callback mCallback; + + private MeasureInWindowOperation( + final int reactTag, + final Callback callback) { + super(); + mReactTag = reactTag; + mCallback = callback; + } + + @Override + public void execute() { + try { + mNativeViewHierarchyManager.measureInWindow(mReactTag, mMeasureBuffer); + } catch (NoSuchNativeViewException e) { + // Invoke with no args to signal failure and to allow JS to clean up the callback + // handle. + mCallback.invoke(); + return; + } + + float x = PixelUtil.toDIPFromPixel(mMeasureBuffer[0]); + float y = PixelUtil.toDIPFromPixel(mMeasureBuffer[1]); + float width = PixelUtil.toDIPFromPixel(mMeasureBuffer[2]); + float height = PixelUtil.toDIPFromPixel(mMeasureBuffer[3]); + mCallback.invoke(x, y, width, height); + } + } + private ArrayList mOperations = new ArrayList<>(); private final class FindTargetForTouchOperation implements UIOperation { @@ -634,6 +666,14 @@ public class UIViewOperationQueue { new MeasureOperation(reactTag, callback)); } + public void enqueueMeasureInWindow( + final int reactTag, + final Callback callback) { + mOperations.add( + new MeasureInWindowOperation(reactTag, callback) + ); + } + public void enqueueFindTargetForTouch( final int reactTag, final float targetX,