From 7ea0ef7a90e18f33cb4e15f673168cee1ab0b64b Mon Sep 17 00:00:00 2001 From: Luna Wei Date: Fri, 18 Jul 2025 14:16:22 -0700 Subject: [PATCH] Implement windowFocus on ReactVirtualViewExperiment (#52690) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52690 Changelog: [Internal] - Add the window focus experiment to ReactVirtualViewExperimental Reviewed By: yungsters Differential Revision: D78502991 fbshipit-source-id: 3e72561835925040e5b240e71734a088908957ed --- .../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 a0a906f6c10..fd5ec0e73db 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 @@ public class ReactScrollView extends ScrollView 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 66bd3d167df..0dae4459618 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