From 6e3307d400f3ff199a61cb3c3ebe93d369fcb4df Mon Sep 17 00:00:00 2001 From: "pengfei.zhou" Date: Mon, 1 Aug 2022 19:01:02 +0800 Subject: [PATCH] feat:fix gesture touch event's conflict in scroller --- .../doric/shader/GestureContainerNode.java | 40 +++-- .../Shader/DoricGestureContainerNode.m | 145 +++++++++++------- 2 files changed, 124 insertions(+), 61 deletions(-) diff --git a/doric-android/doric/src/main/java/pub/doric/shader/GestureContainerNode.java b/doric-android/doric/src/main/java/pub/doric/shader/GestureContainerNode.java index d3797454..7f144cfb 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/GestureContainerNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/GestureContainerNode.java @@ -18,6 +18,8 @@ package pub.doric.shader; import android.annotation.SuppressLint; import android.content.Context; +import android.text.TextUtils; +import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ScaleGestureDetector; @@ -44,7 +46,7 @@ import pub.doric.utils.DoricUtils; @DoricPlugin(name = "GestureContainer") public class GestureContainerNode extends StackNode { - private DoricJSDispatcher jsDispatcher = new DoricJSDispatcher(); + private final DoricJSDispatcher jsDispatcher = new DoricJSDispatcher(); private enum SwipeOrientation { LEFT(0), RIGHT(1), TOP(2), BOTTOM(3); @@ -252,7 +254,7 @@ public class GestureContainerNode extends StackNode { if (onPinch != null) { jsDispatcher.dispatch(new Callable>() { @Override - public AsyncResult call() throws Exception { + public AsyncResult call() { return callJSResponse(onPinch, scaleGestureDetector.getScaleFactor()); } }); @@ -307,8 +309,9 @@ public class GestureContainerNode extends StackNode { } break; case MotionEvent.ACTION_MOVE: - getParent().requestDisallowInterceptTouchEvent(true); - + if (shouldRequestDisallowInterceptTouchEvent(event)) { + getParent().requestDisallowInterceptTouchEvent(true); + } if (onTouchMove != null) { jsDispatcher.dispatch(new Callable>() { @Override @@ -323,6 +326,9 @@ public class GestureContainerNode extends StackNode { break; case MotionEvent.ACTION_UP: + if (shouldRequestDisallowInterceptTouchEvent(event)) { + getParent().requestDisallowInterceptTouchEvent(false); + } if (onTouchUp != null) { callJSResponse(onTouchUp, new JSONBuilder() .put("x", x) @@ -330,8 +336,9 @@ public class GestureContainerNode extends StackNode { .toJSONObject()); } case MotionEvent.ACTION_CANCEL: - getParent().requestDisallowInterceptTouchEvent(false); - + if (shouldRequestDisallowInterceptTouchEvent(event)) { + getParent().requestDisallowInterceptTouchEvent(false); + } if (onTouchCancel != null) { callJSResponse(onTouchCancel, new JSONBuilder() .put("x", x) @@ -343,10 +350,19 @@ public class GestureContainerNode extends StackNode { } // handle gesture - boolean scaleRet = scaleGestureDetector.onTouchEvent(event); - boolean rotateRet = rotateGestureDetector.onTouchEvent(event); + boolean scaleRet = !TextUtils.isEmpty(onPinch) && scaleGestureDetector.onTouchEvent(event); + boolean rotateRet = !TextUtils.isEmpty(onRotate) && rotateGestureDetector.onTouchEvent(event); boolean commonRet = gestureDetector.onTouchEvent(event); - return (scaleRet || rotateRet || commonRet) || super.onTouchEvent(event); + if (scaleRet || rotateRet) { + int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + int action = (event.getAction() & MotionEvent.ACTION_MASK); + if (action == MotionEvent.ACTION_POINTER_DOWN && pointerIndex == 1) { + getParent().requestDisallowInterceptTouchEvent(true); + } else if (action == MotionEvent.ACTION_POINTER_UP && pointerIndex == 1) { + getParent().requestDisallowInterceptTouchEvent(false); + } + } + return (scaleRet || rotateRet || commonRet) || super.onTouchEvent(event) || event.getAction() == MotionEvent.ACTION_DOWN; } private void onSwipeLeft() { @@ -373,4 +389,10 @@ public class GestureContainerNode extends StackNode { } } } + + private boolean shouldRequestDisallowInterceptTouchEvent(MotionEvent event) { + return !TextUtils.isEmpty(onPan) + || !TextUtils.isEmpty(onSwipe) + || !TextUtils.isEmpty(onTouchMove); + } } diff --git a/doric-iOS/Pod/Classes/Shader/DoricGestureContainerNode.m b/doric-iOS/Pod/Classes/Shader/DoricGestureContainerNode.m index 13769557..9902355e 100644 --- a/doric-iOS/Pod/Classes/Shader/DoricGestureContainerNode.m +++ b/doric-iOS/Pod/Classes/Shader/DoricGestureContainerNode.m @@ -24,7 +24,7 @@ #import "DoricGestureContainerNode.h" @interface DoricGestureView : UIView - + @property(nonatomic, weak) DoricGestureContainerNode *node; @end @@ -33,11 +33,11 @@ @implementation DoricGestureView - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesBegan:touches withEvent:event]; - - [touches enumerateObjectsUsingBlock:^(UITouch * _Nonnull obj, BOOL * _Nonnull stop) { + + [touches enumerateObjectsUsingBlock:^(UITouch *_Nonnull obj, BOOL *_Nonnull stop) { if (self.node.onTouchDown) { CGPoint currentLocation = [obj locationInView:self]; - [self.node callJSResponse: self.node.onTouchDown, @{@"x": @(currentLocation.x), @"y": @(currentLocation.y)}, nil]; + [self.node callJSResponse:self.node.onTouchDown, @{@"x": @(currentLocation.x), @"y": @(currentLocation.y)}, nil]; *stop = YES; } }]; @@ -45,8 +45,8 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesMoved:touches withEvent:event]; - - [touches enumerateObjectsUsingBlock:^(UITouch * _Nonnull obj, BOOL * _Nonnull stop) { + + [touches enumerateObjectsUsingBlock:^(UITouch *_Nonnull obj, BOOL *_Nonnull stop) { if (self.node.onTouchMove) { CGPoint currentLocation = [obj locationInView:self]; if (!self.node.jsDispatcher) { @@ -55,7 +55,7 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { __weak typeof(self) __self = self; [self.node.jsDispatcher dispatch:^DoricAsyncResult * { __strong typeof(__self) self = __self; - return [self.node callJSResponse: self.node.onTouchMove, @{@"x": @(currentLocation.x), @"y": @(currentLocation.y)}, nil]; + return [self.node callJSResponse:self.node.onTouchMove, @{@"x": @(currentLocation.x), @"y": @(currentLocation.y)}, nil]; }]; *stop = YES; } @@ -64,11 +64,11 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesEnded:touches withEvent:event]; - - [touches enumerateObjectsUsingBlock:^(UITouch * _Nonnull obj, BOOL * _Nonnull stop) { + + [touches enumerateObjectsUsingBlock:^(UITouch *_Nonnull obj, BOOL *_Nonnull stop) { if (self.node.onTouchUp) { CGPoint currentLocation = [obj locationInView:self]; - [self.node callJSResponse: self.node.onTouchUp, @{@"x": @(currentLocation.x), @"y": @(currentLocation.y)}, nil]; + [self.node callJSResponse:self.node.onTouchUp, @{@"x": @(currentLocation.x), @"y": @(currentLocation.y)}, nil]; *stop = YES; } }]; @@ -76,11 +76,11 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesCancelled:touches withEvent:event]; - - [touches enumerateObjectsUsingBlock:^(UITouch * _Nonnull obj, BOOL * _Nonnull stop) { + + [touches enumerateObjectsUsingBlock:^(UITouch *_Nonnull obj, BOOL *_Nonnull stop) { if (self.node.onTouchCancel) { CGPoint currentLocation = [obj locationInView:self]; - [self.node callJSResponse: self.node.onTouchCancel, @{@"x": @(currentLocation.x), @"y": @(currentLocation.y)}, nil]; + [self.node callJSResponse:self.node.onTouchCancel, @{@"x": @(currentLocation.x), @"y": @(currentLocation.y)}, nil]; *stop = YES; } }]; @@ -88,7 +88,7 @@ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event @end -@interface DoricGestureContainerNode() +@interface DoricGestureContainerNode () @property(nonatomic, strong) NSString *onSingleTap; @property(nonatomic, strong) NSString *onDoubleTap; @@ -100,7 +100,12 @@ @interface DoricGestureContainerNode() @property(nonatomic) CGPoint startPanLocation; @property(nonatomic) CGFloat startRotationDegree; - +@property(nonatomic, strong) UIGestureRecognizer *singleTapGesture; +@property(nonatomic, strong) UITapGestureRecognizer *doubleTapGesture; +@property(nonatomic, strong) UIGestureRecognizer *longPress; +@property(nonatomic, strong) UIGestureRecognizer *pinchGesture; +@property(nonatomic, strong) UIGestureRecognizer *panGesture; +@property(nonatomic, strong) UIGestureRecognizer *rotationGesture; @end #define SWIPE_UP_THRESHOLD -1000.0f @@ -114,45 +119,22 @@ - (UIView *)build { it.doricLayout.layoutType = DoricStack; it.node = self; }]; - - UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapAction:)]; - [stackView addGestureRecognizer:singleTap]; - - UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTapAction:)]; - doubleTap.numberOfTapsRequired = 2; - [stackView addGestureRecognizer:doubleTap]; - - [singleTap requireGestureRecognizerToFail:doubleTap]; - - UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressAction:)]; - [stackView addGestureRecognizer:longPress]; - - UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchAction:)]; - [stackView addGestureRecognizer:pinchGesture]; - - UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)]; - panGesture.cancelsTouchesInView = NO; - [stackView addGestureRecognizer:panGesture]; - - UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotationAction:)]; - [stackView addGestureRecognizer:rotation]; - return stackView; } --(void)singleTapAction:(UITapGestureRecognizer *)sender{ +- (void)singleTapAction:(UITapGestureRecognizer *)sender { if (self.onSingleTap) { [self callJSResponse:self.onSingleTap, nil]; } } --(void)doubleTapAction:(UITapGestureRecognizer *)sender{ +- (void)doubleTapAction:(UITapGestureRecognizer *)sender { if (self.onDoubleTap) { [self callJSResponse:self.onDoubleTap, nil]; } } --(void)longPressAction:(UILongPressGestureRecognizer *)sender{ +- (void)longPressAction:(UILongPressGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateBegan) { if (self.onLongPress) { [self callJSResponse:self.onLongPress, nil]; @@ -160,7 +142,7 @@ -(void)longPressAction:(UILongPressGestureRecognizer *)sender{ } } --(void)pinchAction:(UIPinchGestureRecognizer *)sender{ +- (void)pinchAction:(UIPinchGestureRecognizer *)sender { if (self.onPinch) { if (!self.jsDispatcher) { self.jsDispatcher = [DoricJSDispatcher new]; @@ -173,17 +155,17 @@ -(void)pinchAction:(UIPinchGestureRecognizer *)sender{ } } --(void)panAction:(UIPanGestureRecognizer *)sender{ - if (sender.state == UIGestureRecognizerStateBegan) { +- (void)panAction:(UIPanGestureRecognizer *)sender { + if (sender.state == UIGestureRecognizerStateBegan) { self.startPanLocation = [sender locationInView:self.view]; } CGPoint currentLocation = [sender locationInView:self.view]; - + CGFloat dx = self.startPanLocation.x - currentLocation.x; CGFloat dy = self.startPanLocation.y - currentLocation.y; - + self.startPanLocation = currentLocation; - + if (self.onPan) { if (!self.jsDispatcher) { self.jsDispatcher = [DoricJSDispatcher new]; @@ -194,8 +176,8 @@ -(void)panAction:(UIPanGestureRecognizer *)sender{ return [self callJSResponse:self.onPan, @(dx), @(dy), nil]; }]; } - - + + // detect the swipe gesture if (sender.state == UIGestureRecognizerStateEnded) { CGPoint vel = [sender velocityInView:sender.view]; @@ -226,11 +208,11 @@ -(void)panAction:(UIPanGestureRecognizer *)sender{ } } --(void)rotationAction:(UIRotationGestureRecognizer *)sender{ - if (sender.state == UIGestureRecognizerStateBegan) { +- (void)rotationAction:(UIRotationGestureRecognizer *)sender { + if (sender.state == UIGestureRecognizerStateBegan) { self.startRotationDegree = sender.rotation; } - + CGFloat diffRotation = sender.rotation - self.startRotationDegree; self.startRotationDegree = sender.rotation; if (self.onRotate) { @@ -245,25 +227,84 @@ -(void)rotationAction:(UIRotationGestureRecognizer *)sender{ } } +- (UIGestureRecognizer *)singleTapGesture { + if (_singleTapGesture == nil) { + _singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapAction:)]; + } + return _singleTapGesture; +} + + +- (UITapGestureRecognizer *)doubleTapGesture { + if (_doubleTapGesture == nil) { + _doubleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTapAction:)]; + _doubleTapGesture.numberOfTapsRequired = 2; + } + return _doubleTapGesture; +} + +- (UIGestureRecognizer *)longPress { + if (_longPress == nil) { + _longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressAction:)]; + } + return _longPress; +} + +- (UIGestureRecognizer *)pinchGesture { + if (_pinchGesture == nil) { + _pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchAction:)]; + } + return _pinchGesture; +} + +- (UIGestureRecognizer *)panGesture { + if (_panGesture == nil) { + _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)]; + _panGesture.cancelsTouchesInView = NO; + } + return _panGesture; +} + +- (UIGestureRecognizer *)rotationGesture { + if (_rotationGesture == nil) { + _rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotationAction:)]; + } + return _rotationGesture; +} + - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop { if ([name isEqualToString:@"onSingleTap"]) { self.onSingleTap = prop; + [view addGestureRecognizer:self.singleTapGesture]; } else if ([name isEqualToString:@"onDoubleTap"]) { self.onDoubleTap = prop; + [view removeGestureRecognizer:self.singleTapGesture]; + [view addGestureRecognizer:self.singleTapGesture]; + [view addGestureRecognizer:self.doubleTapGesture]; + [self.singleTapGesture requireGestureRecognizerToFail:self.doubleTapGesture]; } else if ([name isEqualToString:@"onLongPress"]) { self.onLongPress = prop; + [view addGestureRecognizer:self.longPress]; } else if ([name isEqualToString:@"onPinch"]) { self.onPinch = prop; + [view addGestureRecognizer:self.pinchGesture]; } else if ([name isEqualToString:@"onPan"]) { self.onPan = prop; + [view removeGestureRecognizer:self.panGesture]; + [view addGestureRecognizer:self.panGesture]; } else if ([name isEqualToString:@"onRotate"]) { self.onRotate = prop; + [view addGestureRecognizer:self.rotationGesture]; } else if ([name isEqualToString:@"onSwipe"]) { self.onSwipe = prop; + [view removeGestureRecognizer:self.panGesture]; + [view addGestureRecognizer:self.panGesture]; } else if ([name isEqualToString:@"onTouchDown"]) { self.onTouchDown = prop; } else if ([name isEqualToString:@"onTouchMove"]) { self.onTouchMove = prop; + [view removeGestureRecognizer:self.panGesture]; + [view addGestureRecognizer:self.panGesture]; } else if ([name isEqualToString:@"onTouchUp"]) { self.onTouchUp = prop; } else if ([name isEqualToString:@"onTouchCancel"]) {