mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-04 09:27:53 +08:00
Add support for animated events
Summary: This adds support for `Animated.event` driven natively. This is WIP and would like feedback on how this is implemented. At the moment, it works by providing a mapping between a view tag, an event name, an event path and an animated value when a view has a prop with a `AnimatedEvent` object. Then we can hook into `EventDispatcher`, check for events that target our view + event name and update the animated value using the event path. For now it works with the onScroll event but it should be generic enough to work with anything. Closes https://github.com/facebook/react-native/pull/9253 Differential Revision: D3759844 Pulled By: foghina fbshipit-source-id: 86989c705847955bd65e6cf5a7d572ec7ccd3eb4
This commit is contained in:
committed by
Facebook Github Bot 9
parent
19d0429a76
commit
6565929358
@@ -9,6 +9,7 @@ robolectric3_test(
|
||||
react_native_target('java/com/facebook/react/animated:animated'),
|
||||
react_native_target('java/com/facebook/react/bridge:bridge'),
|
||||
react_native_target('java/com/facebook/react/uimanager:uimanager'),
|
||||
react_native_target('java/com/facebook/react/common:common'),
|
||||
react_native_target('java/com/facebook/react:react'),
|
||||
|
||||
react_native_tests_target('java/com/facebook/react/bridge:testhelpers'),
|
||||
|
||||
@@ -14,8 +14,13 @@ import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.JavaOnlyArray;
|
||||
import com.facebook.react.bridge.JavaOnlyMap;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
import com.facebook.react.uimanager.ReactStylesDiffMap;
|
||||
import com.facebook.react.uimanager.UIImplementation;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@@ -56,7 +61,9 @@ public class NativeAnimatedNodeTraversalTest {
|
||||
public PowerMockRule rule = new PowerMockRule();
|
||||
|
||||
private long mFrameTimeNanos;
|
||||
private UIManagerModule mUIManagerMock;
|
||||
private UIImplementation mUIImplementationMock;
|
||||
private EventDispatcher mEventDispatcherMock;
|
||||
private NativeAnimatedNodesManager mNativeAnimatedNodesManager;
|
||||
|
||||
private long nextFrameTime() {
|
||||
@@ -80,8 +87,28 @@ public class NativeAnimatedNodeTraversalTest {
|
||||
});
|
||||
|
||||
mFrameTimeNanos = INITIAL_FRAME_TIME_NANOS;
|
||||
mUIManagerMock = mock(UIManagerModule.class);
|
||||
mUIImplementationMock = mock(UIImplementation.class);
|
||||
mNativeAnimatedNodesManager = new NativeAnimatedNodesManager(mUIImplementationMock);
|
||||
mEventDispatcherMock = mock(EventDispatcher.class);
|
||||
PowerMockito.when(mUIManagerMock.getUIImplementation()).thenAnswer(new Answer<UIImplementation>() {
|
||||
@Override
|
||||
public UIImplementation answer(InvocationOnMock invocation) throws Throwable {
|
||||
return mUIImplementationMock;
|
||||
}
|
||||
});
|
||||
PowerMockito.when(mUIManagerMock.getEventDispatcher()).thenAnswer(new Answer<EventDispatcher>() {
|
||||
@Override
|
||||
public EventDispatcher answer(InvocationOnMock invocation) throws Throwable {
|
||||
return mEventDispatcherMock;
|
||||
}
|
||||
});
|
||||
PowerMockito.when(mUIManagerMock.getConstants()).thenAnswer(new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
return MapBuilder.of("customDirectEventTypes", MapBuilder.newHashMap());
|
||||
}
|
||||
});
|
||||
mNativeAnimatedNodesManager = new NativeAnimatedNodesManager(mUIManagerMock);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -698,4 +725,96 @@ public class NativeAnimatedNodeTraversalTest {
|
||||
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
|
||||
verifyNoMoreInteractions(mUIImplementationMock);
|
||||
}
|
||||
|
||||
private Event createScrollEvent(final int tag, final double value) {
|
||||
return new Event(tag) {
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return "topScroll";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
rctEventEmitter.receiveEvent(tag, "topScroll", JavaOnlyMap.of(
|
||||
"contentOffset", JavaOnlyMap.of("y", value)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNativeAnimatedEventDoUpdate() {
|
||||
int viewTag = 1000;
|
||||
|
||||
createSimpleAnimatedViewWithOpacity(viewTag, 0d);
|
||||
|
||||
mNativeAnimatedNodesManager.addAnimatedEventToView(viewTag, "topScroll", JavaOnlyMap.of(
|
||||
"animatedValueTag", 1,
|
||||
"nativeEventPath", JavaOnlyArray.of("contentOffset", "y")));
|
||||
|
||||
mNativeAnimatedNodesManager.onEventDispatch(createScrollEvent(viewTag, 10));
|
||||
|
||||
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
|
||||
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
|
||||
|
||||
reset(mUIImplementationMock);
|
||||
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
|
||||
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
|
||||
assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNativeAnimatedEventDoNotUpdate() {
|
||||
int viewTag = 1000;
|
||||
|
||||
createSimpleAnimatedViewWithOpacity(viewTag, 0d);
|
||||
|
||||
mNativeAnimatedNodesManager.addAnimatedEventToView(viewTag, "otherEvent", JavaOnlyMap.of(
|
||||
"animatedValueTag", 1,
|
||||
"nativeEventPath", JavaOnlyArray.of("contentOffset", "y")));
|
||||
|
||||
mNativeAnimatedNodesManager.addAnimatedEventToView(999, "topScroll", JavaOnlyMap.of(
|
||||
"animatedValueTag", 1,
|
||||
"nativeEventPath", JavaOnlyArray.of("contentOffset", "y")));
|
||||
|
||||
mNativeAnimatedNodesManager.onEventDispatch(createScrollEvent(viewTag, 10));
|
||||
|
||||
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
|
||||
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
|
||||
|
||||
reset(mUIImplementationMock);
|
||||
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
|
||||
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
|
||||
assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNativeAnimatedEventCustomMapping() {
|
||||
int viewTag = 1000;
|
||||
|
||||
PowerMockito.when(mUIManagerMock.getConstants()).thenAnswer(new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
return MapBuilder.of("customDirectEventTypes", MapBuilder.of(
|
||||
"topScroll", MapBuilder.of("registrationName", "onScroll")
|
||||
));
|
||||
}
|
||||
});
|
||||
mNativeAnimatedNodesManager = new NativeAnimatedNodesManager(mUIManagerMock);
|
||||
|
||||
createSimpleAnimatedViewWithOpacity(viewTag, 0d);
|
||||
|
||||
mNativeAnimatedNodesManager.addAnimatedEventToView(viewTag, "onScroll", JavaOnlyMap.of(
|
||||
"animatedValueTag", 1,
|
||||
"nativeEventPath", JavaOnlyArray.of("contentOffset", "y")));
|
||||
|
||||
mNativeAnimatedNodesManager.onEventDispatch(createScrollEvent(viewTag, 10));
|
||||
|
||||
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
|
||||
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
|
||||
|
||||
reset(mUIImplementationMock);
|
||||
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
|
||||
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
|
||||
assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)).isEqualTo(10);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user