Fix touches with pointer events backtrack
Differential Revision: D2553642 fb-gh-sync-id: b1788879bfbb564a291ada0e7ac206f567780f8a
This commit is contained in:
Родитель
a2524bac33
Коммит
f01272b031
|
@ -11,7 +11,6 @@ package com.facebook.react.uimanager;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -25,8 +24,7 @@ import com.facebook.react.bridge.UiThreadUtil;
|
||||||
*/
|
*/
|
||||||
public class TouchTargetHelper {
|
public class TouchTargetHelper {
|
||||||
|
|
||||||
private static final Rect mVisibleRect = new Rect();
|
private static final float[] mEventCoords = new float[2];
|
||||||
private static final int[] mViewLocationInScreen = {0, 0};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find touch event target view within the provided container given the coordinates provided
|
* Find touch event target view within the provided container given the coordinates provided
|
||||||
|
@ -43,11 +41,15 @@ public class TouchTargetHelper {
|
||||||
ViewGroup viewGroup) {
|
ViewGroup viewGroup) {
|
||||||
UiThreadUtil.assertOnUiThread();
|
UiThreadUtil.assertOnUiThread();
|
||||||
int targetTag = viewGroup.getId();
|
int targetTag = viewGroup.getId();
|
||||||
View nativeTargetView = findTouchTargetView(eventX, eventY, viewGroup);
|
// Store eventCoords in array so that they are modified to be relative to the targetView found.
|
||||||
|
float[] eventCoords = mEventCoords;
|
||||||
|
eventCoords[0] = eventY;
|
||||||
|
eventCoords[1] = eventX;
|
||||||
|
View nativeTargetView = findTouchTargetView(eventCoords, viewGroup);
|
||||||
if (nativeTargetView != null) {
|
if (nativeTargetView != null) {
|
||||||
View reactTargetView = findClosestReactAncestor(nativeTargetView);
|
View reactTargetView = findClosestReactAncestor(nativeTargetView);
|
||||||
if (reactTargetView != null) {
|
if (reactTargetView != null) {
|
||||||
targetTag = getTouchTargetForView(reactTargetView, eventX, eventY);
|
targetTag = getTouchTargetForView(reactTargetView, eventCoords[0], eventCoords[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return targetTag;
|
return targetTag;
|
||||||
|
@ -68,38 +70,36 @@ public class TouchTargetHelper {
|
||||||
* A (pointerEvents: auto) - B (pointerEvents: box-none) - C (pointerEvents: none)
|
* A (pointerEvents: auto) - B (pointerEvents: box-none) - C (pointerEvents: none)
|
||||||
* \ D (pointerEvents: auto) - E (pointerEvents: auto)
|
* \ D (pointerEvents: auto) - E (pointerEvents: auto)
|
||||||
* If the search goes down the first branch, it would return A as the target, which is incorrect.
|
* If the search goes down the first branch, it would return A as the target, which is incorrect.
|
||||||
* NB: This method is not thread-safe as it uses static instance of {@link Rect}
|
* NB: This modifies the eventCoords to always be relative to the current viewGroup. When the
|
||||||
|
* method returns, it will contain the eventCoords relative to the targetView found.
|
||||||
*/
|
*/
|
||||||
private static View findTouchTargetView(float eventX, float eventY, ViewGroup viewGroup) {
|
private static View findTouchTargetView(float[] eventCoords, ViewGroup viewGroup) {
|
||||||
int childrenCount = viewGroup.getChildCount();
|
int childrenCount = viewGroup.getChildCount();
|
||||||
for (int i = childrenCount - 1; i >= 0; i--) {
|
for (int i = childrenCount - 1; i >= 0; i--) {
|
||||||
View child = viewGroup.getChildAt(i);
|
View child = viewGroup.getChildAt(i);
|
||||||
// Views with `removeClippedSubviews` are exposing removed subviews through `getChildAt` to
|
if (isTouchPointInView(eventCoords[0], eventCoords[1], viewGroup, child)) {
|
||||||
// support proper view cleanup. Views removed by this option will be detached from it's
|
|
||||||
// parent, therefore `getGlobalVisibleRect` call will return bogus result as it treat view
|
|
||||||
// with no parent as a root of the view hierarchy. To prevent this from happening we check
|
|
||||||
// that view has a parent before visiting it.
|
|
||||||
if (child.getParent() != null && isTouchPointInView(eventX, eventY, viewGroup, child)) {
|
|
||||||
// Apply offset to event coordinates to transform them into the coordinate space of the
|
// Apply offset to event coordinates to transform them into the coordinate space of the
|
||||||
// child view, taken from {@link ViewGroup#dispatchTransformedTouchEvent()}.
|
// child view, taken from {@link ViewGroup#dispatchTransformedTouchEvent()}.
|
||||||
eventX += viewGroup.getScrollX() - child.getLeft();
|
eventCoords[0] += viewGroup.getScrollY() - child.getTop();
|
||||||
eventY += viewGroup.getScrollY() - child.getTop();
|
eventCoords[1] += viewGroup.getScrollX() - child.getLeft();
|
||||||
View targetView = findTouchTargetViewWithPointerEvents(eventX, eventY, child);
|
View targetView = findTouchTargetViewWithPointerEvents(eventCoords, child);
|
||||||
if (targetView != null) {
|
if (targetView != null) {
|
||||||
return targetView;
|
return targetView;
|
||||||
}
|
}
|
||||||
|
eventCoords[0] -= viewGroup.getScrollY() - child.getTop();
|
||||||
|
eventCoords[1] -= viewGroup.getScrollX() - child.getLeft();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return viewGroup;
|
return viewGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Taken from {@link ViewGroup#isTransformedTouchPointInView()}
|
// Taken from {@link ViewGroup#isTransformedTouchPointInView()}
|
||||||
private static boolean isTouchPointInView(float x, float y, ViewGroup parent, View child) {
|
private static boolean isTouchPointInView(float y, float x, ViewGroup parent, View child) {
|
||||||
float localX = x + parent.getScrollX() - child.getLeft();
|
|
||||||
float localY = y + parent.getScrollY() - child.getTop();
|
float localY = y + parent.getScrollY() - child.getTop();
|
||||||
|
float localX = x + parent.getScrollX() - child.getLeft();
|
||||||
// Taken from {@link View#pointInView()}.
|
// Taken from {@link View#pointInView()}.
|
||||||
return localX >= 0 && localX < (child.getRight() - child.getLeft())
|
return localY >= 0 && localY < (child.getBottom() - child.getTop())
|
||||||
&& localY >= 0 && localY < (child.getBottom() - child.getTop());
|
&& localX >= 0 && localX < (child.getRight() - child.getLeft());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,9 +107,7 @@ public class TouchTargetHelper {
|
||||||
* its descendants are the touch target.
|
* its descendants are the touch target.
|
||||||
*/
|
*/
|
||||||
private static @Nullable View findTouchTargetViewWithPointerEvents(
|
private static @Nullable View findTouchTargetViewWithPointerEvents(
|
||||||
float eventX,
|
float eventCoords[], View view) {
|
||||||
float eventY,
|
|
||||||
View view) {
|
|
||||||
PointerEvents pointerEvents = view instanceof ReactPointerEventsView ?
|
PointerEvents pointerEvents = view instanceof ReactPointerEventsView ?
|
||||||
((ReactPointerEventsView) view).getPointerEvents() : PointerEvents.AUTO;
|
((ReactPointerEventsView) view).getPointerEvents() : PointerEvents.AUTO;
|
||||||
if (pointerEvents == PointerEvents.NONE) {
|
if (pointerEvents == PointerEvents.NONE) {
|
||||||
|
@ -123,7 +121,7 @@ public class TouchTargetHelper {
|
||||||
} else if (pointerEvents == PointerEvents.BOX_NONE) {
|
} else if (pointerEvents == PointerEvents.BOX_NONE) {
|
||||||
// This view can't be the target, but its children might
|
// This view can't be the target, but its children might
|
||||||
if (view instanceof ViewGroup) {
|
if (view instanceof ViewGroup) {
|
||||||
View targetView = findTouchTargetView(eventX, eventY, (ViewGroup) view);
|
View targetView = findTouchTargetView(eventCoords, (ViewGroup) view);
|
||||||
return targetView != view ? targetView : null;
|
return targetView != view ? targetView : null;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -131,7 +129,7 @@ public class TouchTargetHelper {
|
||||||
} else if (pointerEvents == PointerEvents.AUTO) {
|
} else if (pointerEvents == PointerEvents.AUTO) {
|
||||||
// Either this view or one of its children is the target
|
// Either this view or one of its children is the target
|
||||||
if (view instanceof ViewGroup) {
|
if (view instanceof ViewGroup) {
|
||||||
return findTouchTargetView(eventX, eventY, (ViewGroup) view);
|
return findTouchTargetView(eventCoords, (ViewGroup) view);
|
||||||
}
|
}
|
||||||
return view;
|
return view;
|
||||||
|
|
||||||
|
@ -141,14 +139,11 @@ public class TouchTargetHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getTouchTargetForView(View targetView, float eventX, float eventY) {
|
private static int getTouchTargetForView(View targetView, float eventY, float eventX) {
|
||||||
if (targetView instanceof ReactCompoundView) {
|
if (targetView instanceof ReactCompoundView) {
|
||||||
// Use coordinates relative to the view. Use getLocationOnScreen() API, which is slightly more
|
// Use coordinates relative to the view, which have been already computed by
|
||||||
// expensive than getGlobalVisibleRect(), otherwise partially visible views offset is wrong.
|
// {@link #findTouchTargetView()}.
|
||||||
targetView.getLocationOnScreen(mViewLocationInScreen);
|
return ((ReactCompoundView) targetView).reactTagForTouch(eventX, eventY);
|
||||||
return ((ReactCompoundView) targetView).reactTagForTouch(
|
|
||||||
eventX - mViewLocationInScreen[0],
|
|
||||||
eventY - mViewLocationInScreen[1]);
|
|
||||||
}
|
}
|
||||||
return targetView.getId();
|
return targetView.getId();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,6 @@ public class ReactTextView extends TextView implements ReactCompoundView {
|
||||||
int x = (int) touchX;
|
int x = (int) touchX;
|
||||||
int y = (int) touchY;
|
int y = (int) touchY;
|
||||||
|
|
||||||
x -= getTotalPaddingLeft();
|
|
||||||
y -= getTotalPaddingTop();
|
|
||||||
|
|
||||||
x += getScrollX();
|
|
||||||
y += getScrollY();
|
|
||||||
|
|
||||||
Layout layout = getLayout();
|
Layout layout = getLayout();
|
||||||
int line = layout.getLineForVertical(y);
|
int line = layout.getLineForVertical(y);
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче