mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Make the ScrollView connect with the TurboAnimatedModule to fix pressable and animated (#45457)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/45457 With this change, we are implementing in Android a similar logic that we implemented in iOS. 1. When the user stops dragging a scroll view, it tells native animated modle that a scroll has finished 2. NativeAnimated module asks to the NativeAnimatedNodesModule if there are native node listening to the scroll 3. In case they are, it emits an event to JS 4. JS listen to the events and resync the Shadow Tree and the Native Tree (this implemented in a previous change) ## Changelog [Android][Fixed] - Sync the Shadow Tree and the Native Tree with Native animation when scroll is driving the animation Reviewed By: sammy-SC Differential Revision: D59756577 fbshipit-source-id: e558557b477f4da9da1f89fb31ba86d0ea1390a3
This commit is contained in:
committed by
Facebook GitHub Bot
parent
fd748ae84c
commit
394aae1c1c
@@ -476,6 +476,7 @@ public class com/facebook/react/animated/NativeAnimatedModule : com/facebook/fbr
|
||||
public fun stopAnimation (D)V
|
||||
public fun stopListeningToAnimatedNodeValue (D)V
|
||||
public fun updateAnimatedNodeConfig (DLcom/facebook/react/bridge/ReadableMap;)V
|
||||
public fun userDrivenScrollEnded (I)V
|
||||
public fun willDispatchViewUpdates (Lcom/facebook/react/bridge/UIManager;)V
|
||||
public fun willMountItems (Lcom/facebook/react/bridge/UIManager;)V
|
||||
}
|
||||
|
||||
+39
@@ -23,6 +23,7 @@ import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.UIManager;
|
||||
import com.facebook.react.bridge.UIManagerListener;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
|
||||
@@ -38,6 +39,7 @@ import com.facebook.react.uimanager.common.ViewUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@@ -261,6 +263,43 @@ public class NativeAnimatedModule extends NativeAnimatedModuleSpec
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to notify the JS side that the user has stopped scrolling. With natively
|
||||
* driven animation, we might have to force a resync between the Shadow Tree and the Native Tree.
|
||||
* This is because with natively driven animation, the Shadow Tree is bypassed and it can have
|
||||
* stale information on the layout of the native views. This method takes care of verifying if
|
||||
* there are some views listening to the native driven animation and it triggers the resynch.
|
||||
*
|
||||
* @param viewTag The tag of the scroll view that has stopped scrolling
|
||||
*/
|
||||
public void userDrivenScrollEnded(int viewTag) {
|
||||
// ask to the Node Manager for all the native nodes listening to OnScroll event
|
||||
NativeAnimatedNodesManager nodeManager = mNodesManager.get();
|
||||
if (nodeManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Integer> tags = nodeManager.getTagsOfConnectedNodes(viewTag, "topScrollEnded");
|
||||
|
||||
if (tags.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
WritableArray tagsArray = Arguments.createArray();
|
||||
for (Integer tag : tags) {
|
||||
tagsArray.pushInt(tag);
|
||||
}
|
||||
|
||||
// emit the event to JS to resync the trees
|
||||
WritableMap onAnimationEndedData = Arguments.createMap();
|
||||
onAnimationEndedData.putArray("tags", tagsArray);
|
||||
|
||||
ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn();
|
||||
if (reactApplicationContext != null) {
|
||||
reactApplicationContext.emitDeviceEvent("onUserDrivenAnimationEnded", onAnimationEndedData);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
|
||||
+29
@@ -32,10 +32,12 @@ 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.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This is the main class that coordinates how native animated JS implementation drives UI changes.
|
||||
@@ -545,6 +547,11 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener {
|
||||
EventAnimationDriver eventDriver =
|
||||
new EventAnimationDriver(eventName, viewTag, pathList, (ValueAnimatedNode) node);
|
||||
mEventDrivers.add(eventDriver);
|
||||
|
||||
if (eventName.equals("topScroll")) {
|
||||
// Handle the custom topScrollEnded event sent by the ScrollViews when the user stops dragging
|
||||
addAnimatedEventToView(viewTag, "topScrollEnded", eventMapping);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@@ -689,6 +696,28 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener {
|
||||
}
|
||||
}
|
||||
|
||||
Set<Integer> getTagsOfConnectedNodes(int tag, String eventName) {
|
||||
Set<Integer> tags = new HashSet<>();
|
||||
|
||||
// Filter only relevant animation drivers
|
||||
ListIterator<EventAnimationDriver> it = mEventDrivers.listIterator();
|
||||
while (it.hasNext()) {
|
||||
EventAnimationDriver driver = it.next();
|
||||
if (driver != null) {
|
||||
if (eventName.equals(driver.mEventName) && tag == driver.mViewTag) {
|
||||
tags.add(driver.mViewTag);
|
||||
if (driver.mValueNode != null && driver.mValueNode.mChildren != null) {
|
||||
for (AnimatedNode node : driver.mValueNode.mChildren) {
|
||||
tags.add(node.mTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void updateNodes(List<AnimatedNode> nodes) {
|
||||
int activeNodesCount = 0;
|
||||
|
||||
+11
@@ -33,6 +33,8 @@ import androidx.core.view.ViewCompat;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
import com.facebook.react.animated.NativeAnimatedModule;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.build.ReactBuildConfig;
|
||||
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
|
||||
@@ -927,6 +929,15 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
|
||||
if (mSendMomentumEvents) {
|
||||
ReactScrollViewHelper.emitScrollMomentumEndEvent(ReactHorizontalScrollView.this);
|
||||
}
|
||||
|
||||
ReactContext context = (ReactContext) getContext();
|
||||
if (context != null) {
|
||||
NativeAnimatedModule nativeAnimated =
|
||||
context.getNativeModule(NativeAnimatedModule.class);
|
||||
if (nativeAnimated != null) {
|
||||
nativeAnimated.userDrivenScrollEnded(ReactHorizontalScrollView.this.getId());
|
||||
}
|
||||
}
|
||||
disableFpsListener();
|
||||
} else {
|
||||
if (mPagingEnabled && !mSnappingToPage) {
|
||||
|
||||
+10
@@ -35,6 +35,8 @@ import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
import com.facebook.react.R;
|
||||
import com.facebook.react.animated.NativeAnimatedModule;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.uimanager.MeasureSpecAssertions;
|
||||
@@ -701,6 +703,14 @@ public class ReactScrollView extends ScrollView
|
||||
if (mSendMomentumEvents) {
|
||||
ReactScrollViewHelper.emitScrollMomentumEndEvent(ReactScrollView.this);
|
||||
}
|
||||
ReactContext context = (ReactContext) getContext();
|
||||
if (context != null) {
|
||||
NativeAnimatedModule nativeAnimated =
|
||||
context.getNativeModule(NativeAnimatedModule.class);
|
||||
if (nativeAnimated != null) {
|
||||
nativeAnimated.userDrivenScrollEnded(ReactScrollView.this.getId());
|
||||
}
|
||||
}
|
||||
disableFpsListener();
|
||||
} else {
|
||||
if (mPagingEnabled && !mSnappingToPage) {
|
||||
|
||||
Reference in New Issue
Block a user