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.