mirror of
https://github.com/status-im/react-native.git
synced 2025-01-14 11:34:23 +00:00
Native Animated - Support multiple events attached to the same prop
Summary: Fixes a bug that happens when trying to use ScrollView with sticky headers and native `Animated.event` with `onScroll`. Made a few changes to the ListViewPaging UIExplorer example to repro https://gist.github.com/janicduplessis/17e2fcd99c6ea49ced2954d881011b09. What happens is we need to be able to add multiple events to the same prop + viewTag pair. To do that I simple changed the data structure to `Map<prop+viewTag, List<AnimatedEventDriver>>` and try to optimize for the case where there is only one item in the list since it will be the case 99% of the time. **Test plan** Tested by reproducing the bug with the above gist and made sure it was fixed after applying this diff. Closes https://github.com/facebook/react-native/pull/12697 Differential Revision: D4656347 Pulled By: sahrens fbshipit-source-id: b5c36ba796f478e56028c7a95bc0f86bc54cb2ce
This commit is contained in:
parent
59257d6976
commit
c708234f66
@ -2187,7 +2187,13 @@ function attachNativeEvent(viewRef: any, eventName: string, argMapping: Array<?M
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
detach() {
|
detach() {
|
||||||
NativeAnimatedAPI.removeAnimatedEventFromView(viewTag, eventName);
|
eventMappings.forEach((mapping) => {
|
||||||
|
NativeAnimatedAPI.removeAnimatedEventFromView(
|
||||||
|
viewTag,
|
||||||
|
eventName,
|
||||||
|
mapping.animatedValueTag,
|
||||||
|
);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -169,9 +169,10 @@ RCT_EXPORT_METHOD(addAnimatedEventToView:(nonnull NSNumber *)viewTag
|
|||||||
|
|
||||||
RCT_EXPORT_METHOD(removeAnimatedEventFromView:(nonnull NSNumber *)viewTag
|
RCT_EXPORT_METHOD(removeAnimatedEventFromView:(nonnull NSNumber *)viewTag
|
||||||
eventName:(nonnull NSString *)eventName)
|
eventName:(nonnull NSString *)eventName)
|
||||||
|
animatedNodeTag:(nonnull NSNumber *)animatedNodeTag
|
||||||
{
|
{
|
||||||
[_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
[_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||||
[nodesManager removeAnimatedEventFromView:viewTag eventName:eventName];
|
[nodesManager removeAnimatedEventFromView:viewTag eventName:eventName animatedNodeTag:animatedNodeTag];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,8 @@
|
|||||||
eventMapping:(NSDictionary<NSString *, id> *__nonnull)eventMapping;
|
eventMapping:(NSDictionary<NSString *, id> *__nonnull)eventMapping;
|
||||||
|
|
||||||
- (void)removeAnimatedEventFromView:(nonnull NSNumber *)viewTag
|
- (void)removeAnimatedEventFromView:(nonnull NSNumber *)viewTag
|
||||||
eventName:(nonnull NSString *)eventName;
|
eventName:(nonnull NSString *)eventName
|
||||||
|
animatedNodeTag:(nonnull NSNumber *)animatedNodeTag;
|
||||||
|
|
||||||
- (void)handleAnimatedEvent:(nonnull id<RCTEvent>)event;
|
- (void)handleAnimatedEvent:(nonnull id<RCTEvent>)event;
|
||||||
|
|
||||||
|
@ -33,7 +33,9 @@
|
|||||||
{
|
{
|
||||||
RCTUIManager *_uiManager;
|
RCTUIManager *_uiManager;
|
||||||
NSMutableDictionary<NSNumber *, RCTAnimatedNode *> *_animationNodes;
|
NSMutableDictionary<NSNumber *, RCTAnimatedNode *> *_animationNodes;
|
||||||
NSMutableDictionary<NSString *, RCTEventAnimation *> *_eventDrivers;
|
// 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.
|
||||||
|
NSMutableDictionary<NSString *, NSMutableArray<RCTEventAnimation *> *> *_eventDrivers;
|
||||||
NSMutableSet<id<RCTAnimationDriver>> *_activeAnimations;
|
NSMutableSet<id<RCTAnimationDriver>> *_activeAnimations;
|
||||||
CADisplayLink *_displayLink;
|
CADisplayLink *_displayLink;
|
||||||
}
|
}
|
||||||
@ -264,15 +266,36 @@
|
|||||||
NSArray<NSString *> *eventPath = [RCTConvert NSStringArray:eventMapping[@"nativeEventPath"]];
|
NSArray<NSString *> *eventPath = [RCTConvert NSStringArray:eventMapping[@"nativeEventPath"]];
|
||||||
|
|
||||||
RCTEventAnimation *driver =
|
RCTEventAnimation *driver =
|
||||||
[[RCTEventAnimation alloc] initWithEventPath:eventPath valueNode:(RCTValueAnimatedNode *)node];
|
[[RCTEventAnimation alloc] initWithEventPath:eventPath valueNode:(RCTValueAnimatedNode *)node];
|
||||||
|
|
||||||
_eventDrivers[[NSString stringWithFormat:@"%@%@", viewTag, eventName]] = driver;
|
NSString *key = [NSString stringWithFormat:@"%@%@", viewTag, eventName];
|
||||||
|
if (_eventDrivers[key] != nil) {
|
||||||
|
[_eventDrivers[key] addObject:driver];
|
||||||
|
} else {
|
||||||
|
NSMutableArray<RCTEventAnimation *> *drivers = [NSMutableArray new];
|
||||||
|
[drivers addObject:driver];
|
||||||
|
_eventDrivers[key] = drivers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeAnimatedEventFromView:(nonnull NSNumber *)viewTag
|
- (void)removeAnimatedEventFromView:(nonnull NSNumber *)viewTag
|
||||||
eventName:(nonnull NSString *)eventName
|
eventName:(nonnull NSString *)eventName
|
||||||
|
animatedNodeTag:(nonnull NSNumber *)animatedNodeTag
|
||||||
{
|
{
|
||||||
[_eventDrivers removeObjectForKey:[NSString stringWithFormat:@"%@%@", viewTag, eventName]];
|
NSString *key = [NSString stringWithFormat:@"%@%@", viewTag, eventName];
|
||||||
|
if (_eventDrivers[key] != nil) {
|
||||||
|
if (_eventDrivers[key].count == 1) {
|
||||||
|
[_eventDrivers removeObjectForKey:key];
|
||||||
|
} else {
|
||||||
|
NSMutableArray<RCTEventAnimation *> *driversForKey = _eventDrivers[key];
|
||||||
|
for (NSUInteger i = 0; i < driversForKey.count; i++) {
|
||||||
|
if (driversForKey[i].valueNode.nodeTag == animatedNodeTag) {
|
||||||
|
[driversForKey removeObjectAtIndex:i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)handleAnimatedEvent:(id<RCTEvent>)event
|
- (void)handleAnimatedEvent:(id<RCTEvent>)event
|
||||||
@ -282,9 +305,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
NSString *key = [NSString stringWithFormat:@"%@%@", event.viewTag, event.eventName];
|
NSString *key = [NSString stringWithFormat:@"%@%@", event.viewTag, event.eventName];
|
||||||
RCTEventAnimation *driver = _eventDrivers[key];
|
NSMutableArray<RCTEventAnimation *> *driversForKey = _eventDrivers[key];
|
||||||
if (driver) {
|
if (driversForKey) {
|
||||||
[driver updateWithEvent:event];
|
for (RCTEventAnimation *driver in driversForKey) {
|
||||||
|
[driver updateWithEvent:event];
|
||||||
|
}
|
||||||
|
|
||||||
[self updateAnimations];
|
[self updateAnimations];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -351,11 +351,11 @@ public class NativeAnimatedModule extends ReactContextBaseJavaModule implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void removeAnimatedEventFromView(final int viewTag, final String eventName) {
|
public void removeAnimatedEventFromView(final int viewTag, final String eventName, final int animatedValueTag) {
|
||||||
mOperations.add(new UIThreadOperation() {
|
mOperations.add(new UIThreadOperation() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
|
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
|
||||||
animatedNodesManager.removeAnimatedEventFromView(viewTag, eventName);
|
animatedNodesManager.removeAnimatedEventFromView(viewTag, eventName, animatedValueTag);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,9 @@ import java.util.ArrayDeque;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
@ -55,12 +57,14 @@ import javax.annotation.Nullable;
|
|||||||
private final SparseArray<AnimatedNode> mAnimatedNodes = new SparseArray<>();
|
private final SparseArray<AnimatedNode> mAnimatedNodes = new SparseArray<>();
|
||||||
private final SparseArray<AnimationDriver> mActiveAnimations = new SparseArray<>();
|
private final SparseArray<AnimationDriver> mActiveAnimations = new SparseArray<>();
|
||||||
private final SparseArray<AnimatedNode> mUpdatedNodes = new SparseArray<>();
|
private final SparseArray<AnimatedNode> mUpdatedNodes = new SparseArray<>();
|
||||||
private final Map<String, EventAnimationDriver> mEventDrivers = new HashMap<>();
|
// 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<String, List<EventAnimationDriver>> mEventDrivers = new HashMap<>();
|
||||||
private final Map<String, Map<String, String>> mCustomEventTypes;
|
private final Map<String, Map<String, String>> mCustomEventTypes;
|
||||||
private final UIImplementation mUIImplementation;
|
private final UIImplementation mUIImplementation;
|
||||||
private int mAnimatedGraphBFSColor = 0;
|
private int mAnimatedGraphBFSColor = 0;
|
||||||
// Used to avoid allocating a new array on every frame in runUpdates.
|
// Used to avoid allocating a new array on every frame in `runUpdates` and `onEventDispatch`.
|
||||||
private final List<AnimatedNode> mRunUpdateNodeList = new ArrayList<>();
|
private final List<AnimatedNode> mRunUpdateNodeList = new LinkedList<>();
|
||||||
|
|
||||||
public NativeAnimatedNodesManager(UIManagerModule uiManager) {
|
public NativeAnimatedNodesManager(UIManagerModule uiManager) {
|
||||||
mUIImplementation = uiManager.getUIImplementation();
|
mUIImplementation = uiManager.getUIImplementation();
|
||||||
@ -312,11 +316,32 @@ import javax.annotation.Nullable;
|
|||||||
}
|
}
|
||||||
|
|
||||||
EventAnimationDriver event = new EventAnimationDriver(pathList, (ValueAnimatedNode) node);
|
EventAnimationDriver event = new EventAnimationDriver(pathList, (ValueAnimatedNode) node);
|
||||||
mEventDrivers.put(viewTag + eventName, event);
|
String key = viewTag + eventName;
|
||||||
|
if (mEventDrivers.containsKey(key)) {
|
||||||
|
mEventDrivers.get(key).add(event);
|
||||||
|
} else {
|
||||||
|
List<EventAnimationDriver> drivers = new ArrayList<>(1);
|
||||||
|
drivers.add(event);
|
||||||
|
mEventDrivers.put(key, drivers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeAnimatedEventFromView(int viewTag, String eventName) {
|
public void removeAnimatedEventFromView(int viewTag, String eventName, int animatedValueTag) {
|
||||||
mEventDrivers.remove(viewTag + eventName);
|
String key = viewTag + eventName;
|
||||||
|
if (mEventDrivers.containsKey(key)) {
|
||||||
|
List<EventAnimationDriver> driversForKey = mEventDrivers.get(key);
|
||||||
|
if (driversForKey.size() == 1) {
|
||||||
|
mEventDrivers.remove(viewTag + eventName);
|
||||||
|
} else {
|
||||||
|
ListIterator<EventAnimationDriver> it = driversForKey.listIterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
if (it.next().mValueNode.mTag == animatedValueTag) {
|
||||||
|
it.remove();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -334,11 +359,14 @@ import javax.annotation.Nullable;
|
|||||||
eventName = customEventType.get("registrationName");
|
eventName = customEventType.get("registrationName");
|
||||||
}
|
}
|
||||||
|
|
||||||
EventAnimationDriver eventDriver = mEventDrivers.get(event.getViewTag() + eventName);
|
List<EventAnimationDriver> driversForKey = mEventDrivers.get(event.getViewTag() + eventName);
|
||||||
if (eventDriver != null) {
|
if (driversForKey != null) {
|
||||||
event.dispatch(eventDriver);
|
for (EventAnimationDriver driver : driversForKey) {
|
||||||
|
event.dispatch(driver);
|
||||||
updateNodes(Collections.singletonList((AnimatedNode) eventDriver.mValueNode));
|
mRunUpdateNodeList.add(driver.mValueNode);
|
||||||
|
}
|
||||||
|
updateNodes(mRunUpdateNodeList);
|
||||||
|
mRunUpdateNodeList.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user