diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainer.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainer.kt index 042bb378e90..a0212174518 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainer.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainer.kt @@ -50,11 +50,13 @@ private fun rectsOverlap(rect1: Rect, rect2: Rect): Boolean { internal class VirtualViewContainerState { private val prerenderRatio: Double = ReactNativeFeatureFlags.virtualViewPrerenderRatio() + private val hysteresisRatio: Double = ReactNativeFeatureFlags.virtualViewHysteresisRatio() private val virtualViews: MutableSet = mutableSetOf() private val emptyRect: Rect = Rect() private val visibleRect: Rect = Rect() private val prerenderRect: Rect = Rect() + private val hysteresisRect: Rect = Rect() private val onWindowFocusChangeListener = if (ReactNativeFeatureFlags.enableVirtualViewWindowFocusDetection()) { ViewTreeObserver.OnWindowFocusChangeListener { @@ -124,7 +126,7 @@ internal class VirtualViewContainerState { virtualViewsIt.forEach { vv -> val rect = vv.containerRelativeRect - var mode = VirtualViewMode.Hidden + var mode: VirtualViewMode? = VirtualViewMode.Hidden var thresholdRect = emptyRect when { rectsOverlap(rect, visibleRect) -> { @@ -143,14 +145,29 @@ internal class VirtualViewContainerState { mode = VirtualViewMode.Prerender thresholdRect = prerenderRect } - else -> {} + else -> { + if (hysteresisRatio > 0.0) { + hysteresisRect.set(prerenderRect) + hysteresisRect.inset( + (-visibleRect.width() * hysteresisRatio).toInt(), + (-visibleRect.height() * hysteresisRatio).toInt(), + ) + if (rectsOverlap(rect, hysteresisRect)) { + mode = null + } + } + } } - debugLog( - "updateModes", - { "virtualView=${vv.virtualViewID} mode=$mode rect=$rect thresholdRect=$thresholdRect" }, - ) - vv.onModeChange(mode, thresholdRect) + if (mode != null) { + vv.onModeChange(mode, thresholdRect) + debugLog( + "updateModes", + { + "virtualView=${vv.virtualViewID} mode=$mode rect=$rect thresholdRect=$thresholdRect" + }, + ) + } } } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental.kt index 06b1078eea5..865d5127cf1 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental.kt @@ -20,6 +20,7 @@ import com.facebook.react.uimanager.ReactClippingViewGroup import com.facebook.react.uimanager.ReactRoot import com.facebook.react.views.scroll.VirtualView import com.facebook.react.views.scroll.VirtualViewContainer +import com.facebook.react.views.scroll.debugLog import com.facebook.react.views.view.ReactViewGroup import com.facebook.react.views.virtual.VirtualViewMode import com.facebook.react.views.virtual.VirtualViewModeChangeEmitter