Skip to content

Commit 5426539

Browse files
committed
fix(android): prevent gesture activation when outside the visible rect of a view
1 parent 19b70b0 commit 5426539

File tree

2 files changed

+66
-16
lines changed

2 files changed

+66
-16
lines changed

packages/gesturehandler/platforms/android/java/com/swmansion/gesturehandler/GestureHandler.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import android.view.MotionEvent;
66
import android.view.View;
7+
import android.graphics.Rect;
78

89
import java.util.Arrays;
910

@@ -35,6 +36,7 @@ public class GestureHandler<T extends GestureHandler> {
3536
private static int MAX_POINTERS_COUNT = 12;
3637
private static MotionEvent.PointerProperties[] sPointerProps;
3738
private static MotionEvent.PointerCoords[] sPointerCoords;
39+
private static Rect sClipRect = new Rect();
3840

3941
private static void initPointerProps(int size) {
4042
if (sPointerProps == null) {
@@ -63,7 +65,7 @@ private static void initPointerProps(int size) {
6365
private float mLastX, mLastY;
6466
private float mLastEventOffsetX, mLastEventOffsetY;
6567

66-
private boolean mShouldCancelWhenOutside = false;
68+
private boolean mShouldCancelWhenOutside = true;
6769
private int mNumberOfPointers = 0;
6870

6971
private GestureHandlerOrchestrator mOrchestrator;
@@ -402,10 +404,14 @@ public boolean shouldBeCancelledBy(GestureHandler handler) {
402404
}
403405

404406
public boolean isWithinBounds(View view, float posX, float posY) {
405-
float left = 0;
406-
float top = 0;
407-
float right = view.getWidth();
408-
float bottom = view.getHeight();
407+
408+
if (!view.getLocalVisibleRect(sClipRect)) {
409+
return false;
410+
}
411+
float left = sClipRect.left;
412+
float top = sClipRect.top;
413+
float right = sClipRect.right - sClipRect.left;
414+
float bottom = sClipRect.bottom - sClipRect.top;
409415
if (mHitSlop != null) {
410416
float padLeft = mHitSlop[HIT_SLOP_LEFT_IDX];
411417
float padTop = mHitSlop[HIT_SLOP_TOP_IDX];

packages/gesturehandler/platforms/android/java/com/swmansion/gesturehandler/GestureHandlerOrchestrator.java

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import android.graphics.Matrix;
66
import android.graphics.PointF;
7+
import android.graphics.Rect;
78
import android.view.MotionEvent;
89
import android.view.View;
910
import android.view.ViewGroup;
@@ -288,23 +289,61 @@ private void cancelAll() {
288289
mPreparedHandlers[i].cancel();
289290
}
290291
}
292+
/**
293+
* Transforms an event in the coordinates of wrapperView into the coordinate space of the received view.
294+
*
295+
* This modifies and returns the same event as it receives
296+
*
297+
* @param view - view to which coordinate space the event should be transformed
298+
* @param event - event to transform
299+
*/
300+
private MotionEvent transformEventToViewCoords(View view, MotionEvent event) {
301+
if (view == null) {
302+
return event;
303+
}
291304

292-
private void deliverEventToGestureHandler(GestureHandler handler, MotionEvent event) {
293-
if (!isViewAttachedUnderWrapper(handler.getView())) {
305+
ViewGroup parent = null;
306+
if (view.getParent() instanceof ViewGroup) {
307+
parent = (ViewGroup)view.getParent();
308+
}
309+
// Events are passed down to the orchestrator by the wrapperView, so they are already in the
310+
// relevant coordinate space. We want to stop traversing the tree when we reach it.
311+
if (parent != mWrapperView) {
312+
transformEventToViewCoords(parent, event);
313+
}
314+
315+
if (parent != null) {
316+
float localX = event.getX() + parent.getScrollX() - view.getLeft();
317+
float localY = event.getY() + parent.getScrollY() - view.getTop();
318+
event.setLocation(localX, localY);
319+
}
320+
Matrix matrix = view.getMatrix();
321+
if (!matrix.isIdentity()) {
322+
Matrix inverseMatrix = sInverseMatrix;
323+
matrix.invert(inverseMatrix);
324+
event.transform(inverseMatrix);
325+
}
326+
327+
return event;
328+
}
329+
private void deliverEventToGestureHandler(GestureHandler handler, MotionEvent sourceEvent) {
330+
View view = handler.getView();
331+
if (!isViewAttachedUnderWrapper(view)) {
294332
handler.cancel();
295333
return;
296334
}
297-
if (!handler.wantEvents(event)) {
335+
if (!handler.wantEvents(sourceEvent)) {
298336
return;
299337
}
300-
int action = event.getActionMasked();
338+
int action = sourceEvent.getActionMasked();
339+
MotionEvent event = transformEventToViewCoords(view, MotionEvent.obtain(sourceEvent));
301340
if (handler.mIsAwaiting && action == MotionEvent.ACTION_MOVE) {
302341
return;
303342
}
304-
float[] coords = sTempCoords;
305-
extractCoordsForView(handler.getView(), event, coords);
306-
float oldX = event.getX();
307-
float oldY = event.getY();
343+
// float[] coords = sTempCoords;
344+
// extractCoordsForView(view, event, coords);
345+
// float oldX = event.getX();
346+
// float oldY = event.getY();
308347
// TODO: we may conside scaling events if necessary using MotionEvent.transform
309348
// for now the events are only offset to the top left corner of the view but if
310349
// view or any ot the parents is scaled the other pointers position will not
@@ -314,12 +353,12 @@ private void deliverEventToGestureHandler(GestureHandler handler, MotionEvent ev
314353
// approach when we want to use pointer coordinates to calculate velocity or
315354
// distance
316355
// for pinch so I don't know yet if we should transform or not...
317-
event.setLocation(coords[0], coords[1]);
356+
// event.setLocation(coords[0], coords[1]);
318357
handler.handle(event);
319358
if (handler.mIsActive) {
320359
handler.dispatchTouchEvent(event);
321360
}
322-
event.setLocation(oldX, oldY);
361+
// event.setLocation(oldX, oldY);
323362
// if event was of type UP or POINTER_UP we request handler to stop tracking now
324363
// that
325364
// the event has been dispatched
@@ -523,8 +562,13 @@ private boolean isClipping(View view) {
523562
return !(view instanceof ViewGroup) || mViewConfigHelper.isViewClippingChildren((ViewGroup) view);
524563
}
525564

565+
private static Rect sClipRect = new Rect();
566+
526567
private static boolean isTransformedTouchPointInView(float x, float y, View child) {
527-
return x >= 0 && x <= child.getWidth() && y >= 0 && y < child.getHeight();
568+
if (!child.getLocalVisibleRect(sClipRect)) {
569+
return false;
570+
}
571+
return x >= sClipRect.left && x <= sClipRect.right && y >= sClipRect.top && y <= sClipRect.bottom;
528572
}
529573

530574
private static boolean shouldHandlerWaitForOther(GestureHandler handler, GestureHandler other) {

0 commit comments

Comments
 (0)