From 0c2798ca5d0c2e6a7c07ccf8f077f7be5d2bb528 Mon Sep 17 00:00:00 2001 From: Luna Wei Date: Fri, 18 Jul 2025 11:33:11 -0700 Subject: [PATCH 1/2] Use experimental VirtualView in FlatListVirtualColumn (#52689) Summary: Add support for experimental VirtualView in FlatListVirtualColumn Reviewed By: mdvacca Differential Revision: D78450011 --- .../com/facebook/react/views/scroll/VirtualViewContainer.kt | 2 +- .../virtual/viewexperimental/ReactVirtualViewExperimental.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) 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 fa12bc6c85e224..66bd3d167dfe1b 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 @@ -115,7 +115,7 @@ private val IS_DEBUG_BUILD = ReactBuildConfig.DEBUG || ReactBuildConfig.IS_INTERNAL_BUILD || ReactBuildConfig.ENABLE_PERFETTO internal inline fun debugLog(subtag: String, block: () -> String = { "" }) { - if (IS_DEBUG_BUILD) { + if (IS_DEBUG_BUILD && ReactNativeFeatureFlags.enableVirtualViewDebugFeatures()) { FLog.d("$DEBUG_TAG:$subtag", block()) } } 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 42cca02c780c5f..0b819e4e47b902 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 @@ -15,6 +15,7 @@ import androidx.annotation.VisibleForTesting import com.facebook.common.logging.FLog import com.facebook.react.R import com.facebook.react.common.build.ReactBuildConfig +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags import com.facebook.react.uimanager.ReactRoot import com.facebook.react.views.scroll.VirtualView import com.facebook.react.views.scroll.VirtualViewContainer @@ -202,7 +203,7 @@ public class ReactVirtualViewExperimental(context: Context) : } internal inline fun debugLog(subtag: String, block: () -> String = { "" }) { - if (IS_DEBUG_BUILD) { + if (IS_DEBUG_BUILD && ReactNativeFeatureFlags.enableVirtualViewDebugFeatures()) { FLog.d("$DEBUG_TAG:$subtag", "${block()} [$id][$nativeId]") } } From 839abb005fb2a9a795b823e4ebc19a3579e18f71 Mon Sep 17 00:00:00 2001 From: Luna Wei Date: Fri, 18 Jul 2025 11:33:11 -0700 Subject: [PATCH 2/2] Implement windowFocus on ReactVirtualViewExperiment (#52690) Summary: Changelog: [Internal] - Add the window focus experiment to ReactVirtualViewExperimental Reviewed By: yungsters Differential Revision: D78502991 --- .../react/views/scroll/ReactScrollView.java | 3 ++ .../views/scroll/VirtualViewContainer.kt | 36 +++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java index a0a906f6c10493..fd5ec0e73dbd85 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java @@ -407,6 +407,9 @@ protected void onDetachedFromWindow() { if (mMaintainVisibleContentPositionHelper != null) { mMaintainVisibleContentPositionHelper.stop(); } + if (mVirtualViewContainerState != null) { + mVirtualViewContainerState.cleanup(); + } } @Override 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 66bd3d167dfe1b..0dae4459618458 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 @@ -9,6 +9,7 @@ package com.facebook.react.views.scroll import android.graphics.Rect import android.view.ViewGroup +import android.view.ViewTreeObserver import com.facebook.common.logging.FLog import com.facebook.react.common.build.ReactBuildConfig import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags @@ -46,13 +47,34 @@ private fun rectsOverlap(rect1: Rect, rect2: Rect): Boolean { return true } -internal class VirtualViewContainerState(private val scrollView: ViewGroup) { +internal class VirtualViewContainerState { private val prerenderRatio: Double = ReactNativeFeatureFlags.virtualViewPrerenderRatio() + private val detectWindowFocus = ReactNativeFeatureFlags.enableVirtualViewWindowFocusDetection() + private val virtualViews: MutableSet = mutableSetOf() private val emptyRect: Rect = Rect() private val visibleRect: Rect = Rect() private val prerenderRect: Rect = Rect() + private val onWindowFocusChangeListener = + ViewTreeObserver.OnWindowFocusChangeListener { + debugLog("onWindowFocusChanged") + updateModes() + } + private val scrollView: ViewGroup + + constructor(scrollView: ViewGroup) { + this.scrollView = scrollView + if (detectWindowFocus) { + scrollView.viewTreeObserver.addOnWindowFocusChangeListener(onWindowFocusChangeListener) + } + } + + public fun cleanup() { + if (detectWindowFocus) { + scrollView.viewTreeObserver.removeOnWindowFocusChangeListener(onWindowFocusChangeListener) + } + } public fun onChange(virtualView: VirtualView) { if (virtualViews.add(virtualView)) { @@ -72,7 +94,7 @@ internal class VirtualViewContainerState(private val scrollView: ViewGroup) { // Called on ScrollView onLayout or onScroll public fun updateState() { - debugLog("VirtualViewContainer.updateState") + debugLog("updateState") updateModes() } @@ -92,8 +114,16 @@ internal class VirtualViewContainerState(private val scrollView: ViewGroup) { when { rect.isEmpty -> {} rectsOverlap(rect, visibleRect) -> { - mode = VirtualViewMode.Visible thresholdRect = visibleRect + if (detectWindowFocus) { + if (scrollView.hasWindowFocus()) { + mode = VirtualViewMode.Visible + } else { + mode = VirtualViewMode.Prerender + } + } else { + mode = VirtualViewMode.Visible + } } rectsOverlap(rect, prerenderRect) -> { mode = VirtualViewMode.Prerender