From b26f699c9a9278281057bf5952b51249930f4a97 Mon Sep 17 00:00:00 2001 From: Dave Miller Date: Wed, 17 Feb 2016 08:30:24 -0800 Subject: [PATCH] Add measureInWindow on Android Summary:public This adds the capability to get a View in absolute position on the screen, not just relative to the RootView. This is the Android implementation Reviewed By: andreicoman11 Differential Revision: D2939170 fb-gh-sync-id: 658f1ebe6a458088f657a7469389add1a12222cd shipit-source-id: 658f1ebe6a458088f657a7469389add1a12222cd --- .../uimanager/NativeViewHierarchyManager.java | 22 ++++++++++ .../react/uimanager/UIImplementation.java | 13 +++++- .../react/uimanager/UIManagerModule.java | 10 +++++ .../react/uimanager/UIViewOperationQueue.java | 40 +++++++++++++++++++ 4 files changed, 83 insertions(+), 2 deletions(-) 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 aada76ee914..b5939823acf 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 95cd4756e2d..9e63b313fbf 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 9742fc041c2..ddeb94e6db2 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 9b2d8105774..b3b931e0f8e 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,