Skip to content

Commit 4f20e4d

Browse files
lunaleapsfacebook-github-bot
authored andcommitted
Subview clipping for VirtualViewExperiment (#53759)
Summary: Pull Request resolved: #53759 Changelog: [Internal] - Enable subview clipping for experimental version of VirtualView Reviewed By: mdvacca Differential Revision: D82313841 fbshipit-source-id: c6726fd5f443a55ec47c93ef13092fe8bb9297e0
1 parent 09a7e0a commit 4f20e4d

File tree

5 files changed

+62
-6
lines changed

5 files changed

+62
-6
lines changed

packages/react-native/ReactAndroid/api/ReactAndroid.api

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5246,6 +5246,7 @@ public class com/facebook/react/viewmanagers/VirtualViewExperimentalManagerDeleg
52465246

52475247
public abstract interface class com/facebook/react/viewmanagers/VirtualViewExperimentalManagerInterface : com/facebook/react/uimanager/ViewManagerWithGeneratedInterface {
52485248
public abstract fun setInitialHidden (Landroid/view/View;Z)V
5249+
public abstract fun setRemoveClippedSubviews (Landroid/view/View;Z)V
52495250
public abstract fun setRenderState (Landroid/view/View;I)V
52505251
}
52515252

@@ -6725,9 +6726,10 @@ public final class com/facebook/react/views/virtual/viewexperimental/ReactVirtua
67256726
public fun onLayoutChange (Landroid/view/View;IIIIIIII)V
67266727
public fun onModeChange (Lcom/facebook/react/views/virtual/VirtualViewMode;Landroid/graphics/Rect;)V
67276728
public synthetic fun recycleView$xplat_js_react_native_github_packages_react_native_ReactAndroid_src_main_java_com_facebook_react_views_view_viewAndroid ()V
6729+
public fun updateClippingRect (Ljava/util/Set;)V
67286730
}
67296731

6730-
public final class com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimentalManager : com/facebook/react/uimanager/ViewGroupManager, com/facebook/react/viewmanagers/VirtualViewExperimentalManagerInterface {
6732+
public final class com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimentalManager : com/facebook/react/views/view/ReactClippingViewManager, com/facebook/react/viewmanagers/VirtualViewExperimentalManagerInterface {
67316733
public static final field Companion Lcom/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimentalManager$Companion;
67326734
public static final field REACT_CLASS Ljava/lang/String;
67336735
public fun <init> ()V
@@ -6739,6 +6741,7 @@ public final class com/facebook/react/views/virtual/viewexperimental/ReactVirtua
67396741
public fun setInitialHidden (Lcom/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental;Z)V
67406742
public synthetic fun setNativeId (Landroid/view/View;Ljava/lang/String;)V
67416743
public fun setNativeId (Lcom/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental;Ljava/lang/String;)V
6744+
public synthetic fun setRemoveClippedSubviews (Landroid/view/View;Z)V
67426745
public synthetic fun setRenderState (Landroid/view/View;I)V
67436746
public fun setRenderState (Lcom/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental;I)V
67446747
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/VirtualViewContainer.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ internal class VirtualViewContainerState {
119119
(-prerenderRect.height() * prerenderRatio).toInt(),
120120
)
121121

122-
val virtualViewsIt = if (virtualView != null) listOf(virtualView) else virtualViews
122+
val virtualViewsIt =
123+
if (virtualView != null) listOf(virtualView) else virtualViews.toMutableSet()
123124
virtualViewsIt.forEach { vv ->
124125
val rect = vv.containerRelativeRect
125126

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental.kt

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import com.facebook.common.logging.FLog
1616
import com.facebook.react.R
1717
import com.facebook.react.common.build.ReactBuildConfig
1818
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
19+
import com.facebook.react.uimanager.ReactClippingViewGroup
1920
import com.facebook.react.uimanager.ReactRoot
2021
import com.facebook.react.views.scroll.VirtualView
2122
import com.facebook.react.views.scroll.VirtualViewContainer
@@ -34,6 +35,7 @@ public class ReactVirtualViewExperimental(context: Context) :
3435
private var scrollView: VirtualViewContainer? = null
3536

3637
private val lastContainerRelativeRect: Rect = Rect()
38+
private val lastClippingRect: Rect = Rect()
3739
override val containerRelativeRect: Rect = Rect()
3840
private var offsetX: Int = 0
3941
private var offsetY: Int = 0
@@ -120,6 +122,7 @@ public class ReactVirtualViewExperimental(context: Context) :
120122
modeChangeEmitter = null
121123
hadLayout = false
122124
lastContainerRelativeRect.setEmpty()
125+
lastClippingRect.setEmpty()
123126
containerRelativeRect.setEmpty()
124127
}
125128

@@ -132,7 +135,12 @@ public class ReactVirtualViewExperimental(context: Context) :
132135
modeChangeEmitter ?: return
133136
scrollView ?: return
134137

138+
if (newMode == VirtualViewMode.Visible) {
139+
updateClippingRect(null)
140+
}
141+
135142
if (newMode == mode) {
143+
debugLog("onModeChange") { "no change $newMode" }
136144
return
137145
}
138146

@@ -141,6 +149,10 @@ public class ReactVirtualViewExperimental(context: Context) :
141149

142150
debugLog("onModeChange") { "$oldMode->$newMode" }
143151

152+
if (oldMode == VirtualViewMode.Visible) {
153+
updateClippingRect(null)
154+
}
155+
144156
when (newMode) {
145157
VirtualViewMode.Visible -> {
146158
if (renderState == VirtualViewRenderState.Unknown) {
@@ -187,6 +199,37 @@ public class ReactVirtualViewExperimental(context: Context) :
187199
}
188200
}
189201

202+
// Note: We co-opt subview clipping on ReactVirtualView by returning the
203+
// clipping rect of the ScrollView. This means we clip the children of ReactVirtualView
204+
// when they are out of the viewport, but not ReactVirtualView itself.
205+
override fun updateClippingRect(excludedViews: Set<Int>?) {
206+
if (!_removeClippedSubviews) {
207+
return
208+
}
209+
210+
// If no ScrollView, or ScrollView has disabled removeClippedSubviews, use default behavior
211+
if (
212+
scrollView == null ||
213+
!((scrollView as ReactClippingViewGroup)?.removeClippedSubviews ?: false)
214+
) {
215+
super.updateClippingRect(excludedViews)
216+
return
217+
}
218+
219+
val clippingRect = checkNotNull(clippingRect)
220+
221+
(scrollView as ReactClippingViewGroup).getClippingRect(clippingRect)
222+
clippingRect.intersect(containerRelativeRect)
223+
clippingRect.offset(-containerRelativeRect.left, -containerRelativeRect.top)
224+
225+
if (lastClippingRect == clippingRect) {
226+
return
227+
}
228+
229+
updateClippingToRect(clippingRect, excludedViews)
230+
lastClippingRect.set(clippingRect)
231+
}
232+
190233
private fun updateParentOffset() {
191234
val virtualViewScrollView = scrollView ?: return
192235
offsetX = 0
@@ -212,8 +255,11 @@ public class ReactVirtualViewExperimental(context: Context) :
212255
debugLog("reportRectChangeToContainer") { "no rect change $containerRelativeRect" }
213256
return
214257
}
215-
scrollView?.virtualViewContainerState?.onChange(this)
216-
lastContainerRelativeRect.set(containerRelativeRect)
258+
259+
if (scrollView != null) {
260+
scrollView?.virtualViewContainerState?.onChange(this)
261+
lastContainerRelativeRect.set(containerRelativeRect)
262+
}
217263
}
218264

219265
private fun getScrollView(): VirtualViewContainer? = traverseParentStack(true)

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimentalManager.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,20 @@ import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
1313
import com.facebook.react.module.annotations.ReactModule
1414
import com.facebook.react.uimanager.ThemedReactContext
1515
import com.facebook.react.uimanager.UIManagerHelper
16-
import com.facebook.react.uimanager.ViewGroupManager
1716
import com.facebook.react.uimanager.ViewManagerDelegate
1817
import com.facebook.react.uimanager.annotations.ReactProp
1918
import com.facebook.react.uimanager.events.EventDispatcher
2019
import com.facebook.react.viewmanagers.VirtualViewExperimentalManagerDelegate
2120
import com.facebook.react.viewmanagers.VirtualViewExperimentalManagerInterface
21+
import com.facebook.react.views.view.ReactClippingViewManager
2222
import com.facebook.react.views.virtual.VirtualViewMode
2323
import com.facebook.react.views.virtual.VirtualViewModeChangeEmitter
2424
import com.facebook.react.views.virtual.VirtualViewModeChangeEvent
2525
import com.facebook.react.views.virtual.VirtualViewRenderState
2626

2727
@ReactModule(name = ReactVirtualViewExperimentalManager.REACT_CLASS)
2828
public class ReactVirtualViewExperimentalManager :
29-
ViewGroupManager<ReactVirtualViewExperimental>(),
29+
ReactClippingViewManager<ReactVirtualViewExperimental>(),
3030
VirtualViewExperimentalManagerInterface<ReactVirtualViewExperimental> {
3131

3232
private val _delegate = VirtualViewExperimentalManagerDelegate(this)

packages/react-native/src/private/components/virtualview/VirtualViewExperimentalNativeComponent.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ type VirtualViewExperimentalNativeProps = $ReadOnly<{
7979
*/
8080
renderState: Int32,
8181

82+
/**
83+
* This was needed to get VirtualViewManagerDelegate to set this property.
84+
* TODO: Investigate why spread ViewProps doesn't call setter
85+
*/
86+
removeClippedSubviews?: boolean,
87+
8288
/**
8389
* See `NativeModeChangeEvent`.
8490
*/

0 commit comments

Comments
 (0)