From 28d50189f3350e7550bf03ea5bd1363839ee2911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Cargo=C3=ABt?= Date: Mon, 5 Aug 2019 04:36:07 -0700 Subject: [PATCH] Fixed android bounding box (#25836) Summary: This PR fixes https://github.com/facebook/react-native/issues/19637. Summary of the issue: on Android, transformed touchables have press issues because the touchable's measurements are incorrect in the release phase. `UIManager.measure()` returns an incorrect size and position as explained [here](https://github.com/facebook/react-native/issues/19637#issuecomment-396065914) This is easily seen with the inspector : **Screenshot of a { scale: 2 } transform** The real view (scaled) is in pink, the incorrect touchable area is in blue. **Screenshot of a { rotateZ: "-45deg" } transform** The real view (rotated) is in pink, the incorrect touchable area is in blue. ## Changelog [Android] [Fixed] - Fix UIManager.measure() Pull Request resolved: https://github.com/facebook/react-native/pull/25836 Test Plan: Android now produces the same results as iOS as seen on these screenshots | Android without fix | Android with fix | iOS | | --- | --- | --- | | ![Screenshot_1564133103](https://user-images.githubusercontent.com/110431/61941632-28729b00-af98-11e9-9706-b13968b79df5.png) | ![Screenshot_1564130198](https://user-images.githubusercontent.com/110431/61941631-28729b00-af98-11e9-9ff3-5f2748077dbe.png) | ![Simulator Screen Shot - iPhone X - 2019-07-26 at 11 19 34](https://user-images.githubusercontent.com/110431/61941633-28729b00-af98-11e9-8dc4-22b61178242e.png) | Reviewed By: cpojer Differential Revision: D16598914 Pulled By: makovkastar fbshipit-source-id: d56b008b717ea17731fb09001cbd395aa1b044fe --- .../uimanager/NativeViewHierarchyManager.java | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 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 5116111f92745f..bf4908c7c0611e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java @@ -7,6 +7,8 @@ package com.facebook.react.uimanager; import android.content.res.Resources; +import android.graphics.Matrix; +import android.graphics.RectF; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; @@ -74,6 +76,7 @@ public class NativeViewHierarchyManager { private final LayoutAnimationController mLayoutAnimator = new LayoutAnimationController(); private final SparseArray mTagsToPendingIndicesToDelete = new SparseArray<>(); private final int[] mDroppedViewArray = new int[100]; + private final RectF mBoundingBox = new RectF(); private boolean mLayoutAnimationEnabled; private PopupMenu mPopupMenu; @@ -649,16 +652,47 @@ public synchronized void measure(int tag, int[] outputBuffer) { if (rootView == null) { throw new NoSuchNativeViewException("Native view " + tag + " is no longer on screen"); } - rootView.getLocationInWindow(outputBuffer); + computeBoundingBox(rootView, outputBuffer); int rootX = outputBuffer[0]; int rootY = outputBuffer[1]; + computeBoundingBox(v, outputBuffer); + outputBuffer[0] -= rootX; + outputBuffer[1] -= rootY; + } - v.getLocationInWindow(outputBuffer); + private void computeBoundingBox(View view, int[] outputBuffer) { + mBoundingBox.set(0, 0, view.getWidth(), view.getHeight()); + mapRectFromViewToWindowCoords(view, mBoundingBox); - outputBuffer[0] = outputBuffer[0] - rootX; - outputBuffer[1] = outputBuffer[1] - rootY; - outputBuffer[2] = v.getWidth(); - outputBuffer[3] = v.getHeight(); + outputBuffer[0] = Math.round(mBoundingBox.left); + outputBuffer[1] = Math.round(mBoundingBox.top); + outputBuffer[2] = Math.round(mBoundingBox.right - mBoundingBox.left); + outputBuffer[3] = Math.round(mBoundingBox.bottom - mBoundingBox.top); + } + + private void mapRectFromViewToWindowCoords(View view, RectF rect) { + Matrix matrix = view.getMatrix(); + if (!matrix.isIdentity()) { + matrix.mapRect(rect); + } + + rect.offset(view.getLeft(), view.getTop()); + + ViewParent parent = view.getParent(); + while (parent instanceof View) { + View parentView = (View) parent; + + rect.offset(-parentView.getScrollX(), -parentView.getScrollY()); + + matrix = parentView.getMatrix(); + if (!matrix.isIdentity()) { + matrix.mapRect(rect); + } + + rect.offset(parentView.getLeft(), parentView.getTop()); + + parent = parentView.getParent(); + } } /**