From ee31d571b212a4ed120d248e6c89f5ad5d6ec1fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuby=20=28=EC=BB=A4=EB=B9=84!=29?= Date: Fri, 12 Jun 2026 16:26:12 +0900 Subject: [PATCH] fix(Android): use per-display density for px->dp in FabricEnabledViewGroup.updateState Fabric mounts views using the density of the display the surface is attached to, but updateState converted the measured px size back to dp with the process-global density (PixelUtil), captured once from the device's main display. On displays with a different density (Samsung DeX, freeform multi-window, external monitors) every Screen pushed windowSize / mainDisplayDensity into the Shadow Tree, collapsing all stack/tab content into a 1/density-sized box. Convert with the view's own context density instead. On single-display devices both values are identical, so behavior there is unchanged. Fixes #4159 --- .../swmansion/rnscreens/FabricEnabledViewGroup.kt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt b/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt index 78946a3d15..8b19bba4f0 100644 --- a/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt +++ b/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt @@ -28,9 +28,16 @@ abstract class FabricEnabledViewGroup( height: Int, headerHeight: Int, ) { - val realWidth: Float = PixelUtil.toDIPFromPixel(width.toFloat()) - val realHeight: Float = PixelUtil.toDIPFromPixel(height.toFloat()) - val realHeaderHeight: Float = PixelUtil.toDIPFromPixel(headerHeight.toFloat()) + // Use the density of the display this view is attached to, not the process-global one + // captured from the device's main display (PixelUtil). Fabric mounts views using the + // per-display density, so converting back with the global density shrinks/inflates the + // frame pushed to the Shadow Tree whenever the app runs on a display with a different + // density (Samsung DeX, freeform multi-window, external monitors). Both densities are + // identical on single-display devices, so this is a no-op there. See #4159. + val density = context.resources.displayMetrics.density + val realWidth: Float = if (density > 0f) width / density else PixelUtil.toDIPFromPixel(width.toFloat()) + val realHeight: Float = if (density > 0f) height / density else PixelUtil.toDIPFromPixel(height.toFloat()) + val realHeaderHeight: Float = if (density > 0f) headerHeight / density else PixelUtil.toDIPFromPixel(headerHeight.toFloat()) // Check incoming state values. If they're already the correct value, return early to prevent // infinite UpdateState/SetState loop.