diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/EventAnimationDriver.java b/ReactAndroid/src/main/java/com/facebook/react/animated/EventAnimationDriver.java index fce99e38ba4..edc92c238c3 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/EventAnimationDriver.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/EventAnimationDriver.java @@ -21,8 +21,13 @@ import java.util.List; /* package */ class EventAnimationDriver implements RCTEventEmitter { private List mEventPath; /* package */ ValueAnimatedNode mValueNode; + /* package */ String mEventName; + /* package */ int mViewTag; - public EventAnimationDriver(List eventPath, ValueAnimatedNode valueNode) { + public EventAnimationDriver( + String eventName, int viewTag, List eventPath, ValueAnimatedNode valueNode) { + mEventName = eventName; + mViewTag = viewTag; mEventPath = eventPath; mValueNode = valueNode; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java index 9a6953529b4..bb626bed0b2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java @@ -31,11 +31,9 @@ import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.uimanager.events.EventDispatcherListener; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; -import java.util.Map; import java.util.Queue; /** @@ -58,9 +56,9 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener { private final SparseArray mAnimatedNodes = new SparseArray<>(); private final SparseArray mActiveAnimations = new SparseArray<>(); private final SparseArray mUpdatedNodes = new SparseArray<>(); - // Mapping of a view tag and an event name to a list of event animation drivers. 99% of the time - // there will be only one driver per mapping so all code code should be optimized around that. - private final Map> mEventDrivers = new HashMap<>(); + // List of event animation drivers for an event on view. + // There may be multiple drivers for the same event and view. + private final List mEventDrivers = new ArrayList<>(); private final ReactApplicationContext mReactApplicationContext; private int mAnimatedGraphBFSColor = 0; // Used to avoid allocating a new array on every frame in `runUpdates` and `onEventDispatch`. @@ -501,7 +499,8 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener { } @UiThread - public void addAnimatedEventToView(int viewTag, String eventName, ReadableMap eventMapping) { + public void addAnimatedEventToView( + int viewTag, String eventHandlerName, ReadableMap eventMapping) { int nodeTag = eventMapping.getInt("animatedValueTag"); AnimatedNode node = mAnimatedNodes.get(nodeTag); if (node == null) { @@ -512,8 +511,8 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener { throw new JSApplicationIllegalArgumentException( "addAnimatedEventToView: Animated node on view [" + viewTag - + "] connected to event (" - + eventName + + "] connected to event handler (" + + eventHandlerName + ") should be of type " + ValueAnimatedNode.class.getName()); } @@ -524,32 +523,27 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener { pathList.add(path.getString(i)); } - EventAnimationDriver event = new EventAnimationDriver(pathList, (ValueAnimatedNode) node); - String key = viewTag + eventName; - if (mEventDrivers.containsKey(key)) { - mEventDrivers.get(key).add(event); - } else { - List drivers = new ArrayList<>(1); - drivers.add(event); - mEventDrivers.put(key, drivers); - } + String eventName = normalizeEventName(eventHandlerName); + + EventAnimationDriver eventDriver = + new EventAnimationDriver(eventName, viewTag, pathList, (ValueAnimatedNode) node); + mEventDrivers.add(eventDriver); } @UiThread - public void removeAnimatedEventFromView(int viewTag, String eventName, int animatedValueTag) { - String key = viewTag + eventName; - if (mEventDrivers.containsKey(key)) { - List driversForKey = mEventDrivers.get(key); - if (driversForKey.size() == 1) { - mEventDrivers.remove(viewTag + eventName); - } else { - ListIterator it = driversForKey.listIterator(); - while (it.hasNext()) { - if (it.next().mValueNode.mTag == animatedValueTag) { - it.remove(); - break; - } - } + public void removeAnimatedEventFromView( + int viewTag, String eventHandlerName, int animatedValueTag) { + + String eventName = normalizeEventName(eventHandlerName); + + ListIterator it = mEventDrivers.listIterator(); + while (it.hasNext()) { + EventAnimationDriver driver = it.next(); + if (eventName.equals(driver.mEventName) + && viewTag == driver.mViewTag + && animatedValueTag == driver.mValueNode.mTag) { + it.remove(); + break; } } } @@ -585,18 +579,19 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener { if (uiManager == null) { return; } - String eventName = uiManager.resolveCustomDirectEventName(event.getEventName()); - if (eventName == null) { - eventName = ""; - } - List driversForKey = mEventDrivers.get(event.getViewTag() + eventName); - if (driversForKey != null) { - for (EventAnimationDriver driver : driversForKey) { + boolean foundAtLeastOneDriver = false; + Event.EventAnimationDriverMatchSpec matchSpec = event.getEventAnimationDriverMatchSpec(); + for (EventAnimationDriver driver : mEventDrivers) { + if (matchSpec.match(driver.mViewTag, driver.mEventName)) { + foundAtLeastOneDriver = true; stopAnimationsForNode(driver.mValueNode); event.dispatch(driver); mRunUpdateNodeList.add(driver.mValueNode); } + } + + if (foundAtLeastOneDriver) { updateNodes(mRunUpdateNodeList); mRunUpdateNodeList.clear(); } @@ -824,4 +819,14 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener { mWarnedAboutGraphTraversal = false; } } + + private String normalizeEventName(String eventHandlerName) { + // Fabric UIManager also makes this assumption + String eventName = eventHandlerName; + if (eventHandlerName.startsWith("on")) { + eventName = "top" + eventHandlerName.substring(2); + } + + return eventName; + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java index 0b6a1140c8e..01b355def8b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java @@ -40,6 +40,7 @@ public abstract class Event { private int mViewTag; private long mTimestampMs; private int mUniqueID = sUniqueID++; + private @Nullable EventAnimationDriverMatchSpec mEventAnimationDriverMatchSpec; protected Event() {} @@ -165,6 +166,19 @@ public abstract class Event { /** @return the name of this event as registered in JS */ public abstract String getEventName(); + public EventAnimationDriverMatchSpec getEventAnimationDriverMatchSpec() { + if (mEventAnimationDriverMatchSpec == null) { + mEventAnimationDriverMatchSpec = + new EventAnimationDriverMatchSpec() { + @Override + public boolean match(int viewTag, String eventName) { + return viewTag == getViewTag() && eventName.equals(getEventName()); + }; + }; + } + return mEventAnimationDriverMatchSpec; + } + /** * Dispatch this event to JS using the given event emitter. Compatible with old and new renderer. * Instead of using this or dispatchModern, it is recommended that you simply override @@ -225,4 +239,8 @@ public abstract class Event { } dispatch(rctEventEmitter); } + + public interface EventAnimationDriverMatchSpec { + boolean match(int viewTag, String eventName); + } }