Skip to content

Commit 40769f2

Browse files
Luna Weifacebook-github-bot
Luna Wei
authored andcommitted
PointerEvents: Don't dispatch when no listeners for hover events
Summary: Changelog: [Internal][Changed] - Make the same optimization on enter/leave/move pointer events being dispatched by a mouse input. If any ancestor view is listening to enter/leave events (just capture) then we dispatch the enter/leave event. Reviewed By: vincentriemer Differential Revision: D36601638 fbshipit-source-id: d6b5c32ae50bcf000100bcb878ca2ca89bd5c02e
1 parent 0ad38a0 commit 40769f2

File tree

1 file changed

+83
-41
lines changed

1 file changed

+83
-41
lines changed

ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java

Lines changed: 83 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,13 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp
111111

112112
if (!supportsHover) {
113113
dispatchNonBubblingEventForPathWhenListened(
114-
EVENT.ENTER, EVENT.ENTER_CAPTURE, hitPath, eventDispatcher, surfaceId, motionEvent);
114+
EVENT.ENTER,
115+
EVENT.ENTER_CAPTURE,
116+
hitPath,
117+
eventDispatcher,
118+
surfaceId,
119+
motionEvent,
120+
false);
115121
}
116122

117123
boolean listeningForDown =
@@ -196,7 +202,13 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp
196202

197203
if (!supportsHover) {
198204
dispatchNonBubblingEventForPathWhenListened(
199-
EVENT.LEAVE, EVENT.LEAVE_CAPTURE, hitPath, eventDispatcher, surfaceId, motionEvent);
205+
EVENT.LEAVE,
206+
EVENT.LEAVE_CAPTURE,
207+
hitPath,
208+
eventDispatcher,
209+
surfaceId,
210+
motionEvent,
211+
false);
200212
}
201213
return;
202214
}
@@ -227,22 +239,28 @@ private static boolean isAnyoneListeningForBubblingEvent(
227239
return false;
228240
}
229241

230-
/*
231-
Dispatch event only if ancestor is listening to relevant event.
232-
This should only be relevant for ENTER/LEAVE events.
233-
@param hitPath - ordered from inner target to root
242+
/**
243+
* Dispatch event only if ancestor is listening to relevant capture event. This should only be
244+
* relevant for ENTER/LEAVE events that need to be dispatched along every relevant view in the hit
245+
* path.
246+
*
247+
* @param pointerEventType - Should only be ENTER/LEAVE events
248+
* @param hitPath - ViewTargets ordered from target -> root
249+
* @param dispatcher
250+
* @param surfaceId
251+
* @param motionEvent
252+
* @param forceDispatch - Ignore if ancestor is listening and force the event to be dispatched
234253
*/
235-
236-
/** Dispatch non-bubbling event along the hit path only when relevant listeners */
237254
private static void dispatchNonBubblingEventForPathWhenListened(
238255
EVENT event,
239256
EVENT captureEvent,
240257
List<ViewTarget> hitPath,
241258
EventDispatcher dispatcher,
242259
int surfaceId,
243-
MotionEvent motionEvent) {
260+
MotionEvent motionEvent,
261+
boolean forceDispatch) {
244262

245-
boolean ancestorListening = false;
263+
boolean ancestorListening = forceDispatch;
246264
String eventName = PointerEventHelper.getDispatchableEventName(event);
247265
if (eventName == null) {
248266
return;
@@ -312,54 +330,72 @@ private void handleHoverEvent(
312330
// hitState is list ordered from inner child -> parent tag
313331
// Traverse hitState back-to-front to find the first divergence with mLastHitState
314332
// FIXME: this may generate incorrect events when view collapsing changes the hierarchy
315-
int firstDivergentIndex = 0;
316-
while (firstDivergentIndex < Math.min(hitPath.size(), mLastHitPath.size())
333+
boolean nonDivergentListeningToEnter = false;
334+
boolean nonDivergentListeningToLeave = false;
335+
int firstDivergentIndexFromBack = 0;
336+
while (firstDivergentIndexFromBack < Math.min(hitPath.size(), mLastHitPath.size())
317337
&& hitPath
318-
.get(hitPath.size() - 1 - firstDivergentIndex)
319-
.equals(mLastHitPath.get(mLastHitPath.size() - 1 - firstDivergentIndex))) {
320-
firstDivergentIndex++;
338+
.get(hitPath.size() - 1 - firstDivergentIndexFromBack)
339+
.equals(mLastHitPath.get(mLastHitPath.size() - 1 - firstDivergentIndexFromBack))) {
340+
341+
// Track if any non-diverging views are listening to enter/leave
342+
View nonDivergentViewTargetView =
343+
hitPath.get(hitPath.size() - 1 - firstDivergentIndexFromBack).getView();
344+
if (!nonDivergentListeningToEnter
345+
&& PointerEventHelper.isListening(nonDivergentViewTargetView, EVENT.ENTER_CAPTURE)) {
346+
nonDivergentListeningToEnter = true;
347+
}
348+
if (!nonDivergentListeningToLeave
349+
&& PointerEventHelper.isListening(nonDivergentViewTargetView, EVENT.LEAVE_CAPTURE)) {
350+
nonDivergentListeningToLeave = true;
351+
}
352+
353+
firstDivergentIndexFromBack++;
321354
}
322355

323-
boolean hasDiverged = firstDivergentIndex < Math.max(hitPath.size(), mLastHitPath.size());
356+
boolean hasDiverged =
357+
firstDivergentIndexFromBack < Math.max(hitPath.size(), mLastHitPath.size());
324358

325-
// Fire all relevant enter events
326359
if (hasDiverged) {
327360
// If something has changed in either enter/exit, let's start a new coalescing key
328361
mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mHoverInteractionKey);
329362

330-
List<ViewTarget> enterViewTargets = hitPath.subList(0, hitPath.size() - firstDivergentIndex);
363+
List<ViewTarget> enterViewTargets =
364+
hitPath.subList(0, hitPath.size() - firstDivergentIndexFromBack);
331365
if (enterViewTargets.size() > 0) {
332-
// root -> child
333-
for (int i = enterViewTargets.size(); i-- > 0; ) {
334-
eventDispatcher.dispatchEvent(
335-
PointerEvent.obtain(
336-
PointerEventHelper.POINTER_ENTER,
337-
surfaceId,
338-
enterViewTargets.get(i).getViewId(),
339-
motionEvent));
340-
}
366+
dispatchNonBubblingEventForPathWhenListened(
367+
EVENT.ENTER,
368+
EVENT.ENTER_CAPTURE,
369+
enterViewTargets,
370+
eventDispatcher,
371+
surfaceId,
372+
motionEvent,
373+
nonDivergentListeningToEnter);
341374
}
342375

343-
// Fire all relevant exit events
344376
List<ViewTarget> exitViewTargets =
345-
mLastHitPath.subList(0, mLastHitPath.size() - firstDivergentIndex);
377+
mLastHitPath.subList(0, mLastHitPath.size() - firstDivergentIndexFromBack);
346378
if (exitViewTargets.size() > 0) {
347379
// child -> root
348-
for (ViewTarget exitViewTarget : exitViewTargets) {
349-
eventDispatcher.dispatchEvent(
350-
PointerEvent.obtain(
351-
PointerEventHelper.POINTER_LEAVE,
352-
surfaceId,
353-
exitViewTarget.getViewId(),
354-
motionEvent));
355-
}
380+
dispatchNonBubblingEventForPathWhenListened(
381+
EVENT.LEAVE,
382+
EVENT.LEAVE_CAPTURE,
383+
enterViewTargets,
384+
eventDispatcher,
385+
surfaceId,
386+
motionEvent,
387+
nonDivergentListeningToLeave);
356388
}
357389
}
358390

359391
int coalescingKey = mTouchEventCoalescingKeyHelper.getCoalescingKey(mHoverInteractionKey);
360-
eventDispatcher.dispatchEvent(
361-
PointerEvent.obtain(
362-
PointerEventHelper.POINTER_MOVE, surfaceId, targetTag, motionEvent, coalescingKey));
392+
boolean listeningToMove =
393+
isAnyoneListeningForBubblingEvent(hitPath, EVENT.MOVE, EVENT.MOVE_CAPTURE);
394+
if (listeningToMove) {
395+
eventDispatcher.dispatchEvent(
396+
PointerEvent.obtain(
397+
PointerEventHelper.POINTER_MOVE, surfaceId, targetTag, motionEvent, coalescingKey));
398+
}
363399

364400
mLastHitPath = hitPath;
365401
mLastEventCoordinates[0] = x;
@@ -389,7 +425,13 @@ private void dispatchCancelEvent(
389425
}
390426

391427
dispatchNonBubblingEventForPathWhenListened(
392-
EVENT.LEAVE, EVENT.LEAVE_CAPTURE, hitPath, eventDispatcher, surfaceId, motionEvent);
428+
EVENT.LEAVE,
429+
EVENT.LEAVE_CAPTURE,
430+
hitPath,
431+
eventDispatcher,
432+
surfaceId,
433+
motionEvent,
434+
false);
393435

394436
mTouchEventCoalescingKeyHelper.removeCoalescingKey(mDownStartTime);
395437
mDownStartTime = TouchEvent.UNSET;

0 commit comments

Comments
 (0)