PointerEvents: Fix dispatch optimization
Summary: Changelog: [Internal] Fixing a recent optimization to prevent event dispatches for events that are not listened for. An incorrect hitpath was passed in for `leave` events. Refactored the PointerEvent optimization such that `filterByShouldDispatch` determines what views should dispatch a PointerEvent, and `dispatchEventForViewTargets` to actually dispatch them. We are separating this because the order of dispatch differs between `enter` and `leave` events. Reviewed By: vincentriemer Differential Revision: D37348726 fbshipit-source-id: a09a04df3ae027cce95e0d93a4163c2015fe3fe3
This commit is contained in:
Родитель
15d9aa0cdb
Коммит
fa814d4875
|
@ -20,6 +20,7 @@ import com.facebook.react.uimanager.events.PointerEventHelper;
|
||||||
import com.facebook.react.uimanager.events.PointerEventHelper.EVENT;
|
import com.facebook.react.uimanager.events.PointerEventHelper.EVENT;
|
||||||
import com.facebook.react.uimanager.events.TouchEvent;
|
import com.facebook.react.uimanager.events.TouchEvent;
|
||||||
import com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper;
|
import com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -110,14 +111,17 @@ public class JSPointerDispatcher {
|
||||||
mTouchEventCoalescingKeyHelper.addCoalescingKey(mDownStartTime);
|
mTouchEventCoalescingKeyHelper.addCoalescingKey(mDownStartTime);
|
||||||
|
|
||||||
if (!supportsHover) {
|
if (!supportsHover) {
|
||||||
dispatchNonBubblingEventForPathWhenListened(
|
List<ViewTarget> enterViewTargets =
|
||||||
EVENT.ENTER,
|
filterByShouldDispatch(hitPath, EVENT.ENTER, EVENT.ENTER_CAPTURE, false);
|
||||||
EVENT.ENTER_CAPTURE,
|
|
||||||
hitPath,
|
// Dispatch root -> target, we need to reverse order of enterViewTargets
|
||||||
|
Collections.reverse(enterViewTargets);
|
||||||
|
dispatchEventForViewTargets(
|
||||||
|
PointerEventHelper.POINTER_ENTER,
|
||||||
|
enterViewTargets,
|
||||||
eventDispatcher,
|
eventDispatcher,
|
||||||
surfaceId,
|
surfaceId,
|
||||||
motionEvent,
|
motionEvent);
|
||||||
false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean listeningForDown =
|
boolean listeningForDown =
|
||||||
|
@ -201,14 +205,16 @@ public class JSPointerDispatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!supportsHover) {
|
if (!supportsHover) {
|
||||||
dispatchNonBubblingEventForPathWhenListened(
|
List<ViewTarget> leaveViewTargets =
|
||||||
EVENT.LEAVE,
|
filterByShouldDispatch(hitPath, EVENT.LEAVE, EVENT.LEAVE_CAPTURE, false);
|
||||||
EVENT.LEAVE_CAPTURE,
|
|
||||||
hitPath,
|
// target -> root
|
||||||
|
dispatchEventForViewTargets(
|
||||||
|
PointerEventHelper.POINTER_LEAVE,
|
||||||
|
leaveViewTargets,
|
||||||
eventDispatcher,
|
eventDispatcher,
|
||||||
surfaceId,
|
surfaceId,
|
||||||
motionEvent,
|
motionEvent);
|
||||||
false);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -240,43 +246,47 @@ public class JSPointerDispatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatch event only if ancestor is listening to relevant capture event. This should only be
|
* Returns list of view targets that we should be dispatching events from
|
||||||
* relevant for ENTER/LEAVE events that need to be dispatched along every relevant view in the hit
|
|
||||||
* path.
|
|
||||||
*
|
*
|
||||||
* @param pointerEventType - Should only be ENTER/LEAVE events
|
* @param viewTargets, ordered from target -> root
|
||||||
* @param hitPath - ViewTargets ordered from target -> root
|
* @param bubble, name of event that bubbles
|
||||||
* @param dispatcher
|
* @param capture, name of event that captures
|
||||||
* @param surfaceId
|
* @param forceDispatch, if true, all viewTargets should dispatch
|
||||||
* @param motionEvent
|
* @return list of viewTargets filtered from target -> root
|
||||||
* @param forceDispatch - Ignore if ancestor is listening and force the event to be dispatched
|
|
||||||
*/
|
*/
|
||||||
private static void dispatchNonBubblingEventForPathWhenListened(
|
private static List<ViewTarget> filterByShouldDispatch(
|
||||||
EVENT event,
|
List<ViewTarget> viewTargets, EVENT bubble, EVENT capture, boolean forceDispatch) {
|
||||||
EVENT captureEvent,
|
if (forceDispatch) {
|
||||||
List<ViewTarget> hitPath,
|
return viewTargets;
|
||||||
EventDispatcher dispatcher,
|
|
||||||
int surfaceId,
|
|
||||||
MotionEvent motionEvent,
|
|
||||||
boolean forceDispatch) {
|
|
||||||
|
|
||||||
boolean ancestorListening = forceDispatch;
|
|
||||||
String eventName = PointerEventHelper.getDispatchableEventName(event);
|
|
||||||
if (eventName == null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate through hitPath from ancestor -> target
|
boolean ancestorListening = false;
|
||||||
for (int i = hitPath.size() - 1; i >= 0; i--) {
|
List<ViewTarget> dispatchableViewTargets = new ArrayList<ViewTarget>(viewTargets);
|
||||||
View view = hitPath.get(i).getView();
|
for (int i = viewTargets.size() - 1; i >= 0; i--) {
|
||||||
int viewId = hitPath.get(i).getViewId();
|
ViewTarget viewTarget = viewTargets.get(i);
|
||||||
if (ancestorListening
|
View view = viewTarget.getView();
|
||||||
|| (i == 0 && PointerEventHelper.isListening(view, event))
|
if (!ancestorListening
|
||||||
|| PointerEventHelper.isListening(view, captureEvent)) {
|
&& (PointerEventHelper.isListening(view, capture)
|
||||||
dispatcher.dispatchEvent(PointerEvent.obtain(eventName, surfaceId, viewId, motionEvent));
|
|| (i == 0 && PointerEventHelper.isListening(view, bubble)))) {
|
||||||
ancestorListening = true;
|
ancestorListening = true;
|
||||||
|
} else if (!ancestorListening) {
|
||||||
|
dispatchableViewTargets.remove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return dispatchableViewTargets;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void dispatchEventForViewTargets(
|
||||||
|
String eventName,
|
||||||
|
List<ViewTarget> viewTargets,
|
||||||
|
EventDispatcher dispatcher,
|
||||||
|
int surfaceId,
|
||||||
|
MotionEvent motionEvent) {
|
||||||
|
|
||||||
|
for (ViewTarget viewTarget : viewTargets) {
|
||||||
|
int viewId = viewTarget.getViewId();
|
||||||
|
dispatcher.dispatchEvent(PointerEvent.obtain(eventName, surfaceId, viewId, motionEvent));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// called on hover_move motion events only
|
// called on hover_move motion events only
|
||||||
|
@ -360,31 +370,40 @@ public class JSPointerDispatcher {
|
||||||
// If something has changed in either enter/exit, let's start a new coalescing key
|
// If something has changed in either enter/exit, let's start a new coalescing key
|
||||||
mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mHoverInteractionKey);
|
mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mHoverInteractionKey);
|
||||||
|
|
||||||
|
// target -> root
|
||||||
List<ViewTarget> enterViewTargets =
|
List<ViewTarget> enterViewTargets =
|
||||||
hitPath.subList(0, hitPath.size() - firstDivergentIndexFromBack);
|
filterByShouldDispatch(
|
||||||
|
hitPath.subList(0, hitPath.size() - firstDivergentIndexFromBack),
|
||||||
|
EVENT.ENTER,
|
||||||
|
EVENT.ENTER_CAPTURE,
|
||||||
|
nonDivergentListeningToEnter);
|
||||||
|
|
||||||
if (enterViewTargets.size() > 0) {
|
if (enterViewTargets.size() > 0) {
|
||||||
dispatchNonBubblingEventForPathWhenListened(
|
// We want to iterate these from root -> target so we need to reverse
|
||||||
EVENT.ENTER,
|
Collections.reverse(enterViewTargets);
|
||||||
EVENT.ENTER_CAPTURE,
|
dispatchEventForViewTargets(
|
||||||
|
PointerEventHelper.POINTER_ENTER,
|
||||||
enterViewTargets,
|
enterViewTargets,
|
||||||
eventDispatcher,
|
eventDispatcher,
|
||||||
surfaceId,
|
surfaceId,
|
||||||
motionEvent,
|
motionEvent);
|
||||||
nonDivergentListeningToEnter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ViewTarget> exitViewTargets =
|
// target -> root
|
||||||
mLastHitPath.subList(0, mLastHitPath.size() - firstDivergentIndexFromBack);
|
List<ViewTarget> leaveViewTargets =
|
||||||
if (exitViewTargets.size() > 0) {
|
filterByShouldDispatch(
|
||||||
// child -> root
|
mLastHitPath.subList(0, mLastHitPath.size() - firstDivergentIndexFromBack),
|
||||||
dispatchNonBubblingEventForPathWhenListened(
|
EVENT.LEAVE,
|
||||||
EVENT.LEAVE,
|
EVENT.LEAVE_CAPTURE,
|
||||||
EVENT.LEAVE_CAPTURE,
|
nonDivergentListeningToLeave);
|
||||||
enterViewTargets,
|
if (leaveViewTargets.size() > 0) {
|
||||||
|
// We want to dispatch from target -> root, so no need to reverse
|
||||||
|
dispatchEventForViewTargets(
|
||||||
|
PointerEventHelper.POINTER_LEAVE,
|
||||||
|
leaveViewTargets,
|
||||||
eventDispatcher,
|
eventDispatcher,
|
||||||
surfaceId,
|
surfaceId,
|
||||||
motionEvent,
|
motionEvent);
|
||||||
nonDivergentListeningToLeave);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,14 +443,16 @@ public class JSPointerDispatcher {
|
||||||
PointerEventHelper.POINTER_CANCEL, surfaceId, targetTag, motionEvent));
|
PointerEventHelper.POINTER_CANCEL, surfaceId, targetTag, motionEvent));
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchNonBubblingEventForPathWhenListened(
|
List<ViewTarget> leaveViewTargets =
|
||||||
EVENT.LEAVE,
|
filterByShouldDispatch(hitPath, EVENT.LEAVE, EVENT.LEAVE_CAPTURE, false);
|
||||||
EVENT.LEAVE_CAPTURE,
|
|
||||||
hitPath,
|
// dispatch from target -> root
|
||||||
|
dispatchEventForViewTargets(
|
||||||
|
PointerEventHelper.POINTER_LEAVE,
|
||||||
|
leaveViewTargets,
|
||||||
eventDispatcher,
|
eventDispatcher,
|
||||||
surfaceId,
|
surfaceId,
|
||||||
motionEvent,
|
motionEvent);
|
||||||
false);
|
|
||||||
|
|
||||||
mTouchEventCoalescingKeyHelper.removeCoalescingKey(mDownStartTime);
|
mTouchEventCoalescingKeyHelper.removeCoalescingKey(mDownStartTime);
|
||||||
mDownStartTime = TouchEvent.UNSET;
|
mDownStartTime = TouchEvent.UNSET;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче