From 90bd0467f9bcee3294875f1d1569d9487f31256e Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Mon, 3 Feb 2025 19:01:14 +0100 Subject: [PATCH 1/3] feat: support `contentInset` on Android --- .../react/views/scroll/ReactScrollView.java | 46 +++++++++++++++++++ .../views/scroll/ReactScrollViewManager.java | 6 +++ 2 files changed, 52 insertions(+) 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 71acfa6288b2b5..44657248aa7546 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 @@ -1109,6 +1109,52 @@ public void setContentOffset(ReadableMap value) { } } + public void setContentInset(ReadableMap insets) { + int left = 0; + int top = 0; + int right = 0; + int bottom = 0; + if (insets != null) { + if (insets.hasKey("left")) { + left = (int) PixelUtil.toPixelFromDIP(insets.getDouble("left")); + } + if (insets.hasKey("top")) { + top = (int) PixelUtil.toPixelFromDIP(insets.getDouble("top")); + } + if (insets.hasKey("right")) { + right = (int) PixelUtil.toPixelFromDIP(insets.getDouble("right")); + } + if (insets.hasKey("bottom")) { + bottom = (int) PixelUtil.toPixelFromDIP(insets.getDouble("bottom")); + } + } + + setPadding(left, top, right, bottom); + + // If clipping of subviews is enabled, update the clipping rect + if (mRemoveClippedSubviews) { + updateClippingRect(); + } + + // Force a layout pass to ensure the new padding is applied. + requestLayout(); + invalidate(); + + // Post a runnable to adjust the scroll position if needed. + // (Using post() ensures this happens after the layout pass.) + post(new Runnable() { + @Override + public void run() { + int maxScrollY = getMaxScrollY(); + // If the current scroll offset is now beyond the new maximum, + // adjust it to the new maximum. + if (getScrollY() > maxScrollY) { + scrollTo(getScrollX(), maxScrollY); + } + } + }); + } + /** * Calls `smoothScrollTo` and updates state. * diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java index b9260c5ca5a627..0d0f1465677aaa 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java @@ -327,6 +327,12 @@ public void setContentOffset(ReactScrollView view, ReadableMap value) { view.setContentOffset(value); } + @ReactProp(name = "contentInset") + public void setContentInset(ReactScrollView view, ReadableMap value) { + view.setContentInset(value); + } + + @ReactProp(name = "maintainVisibleContentPosition") public void setMaintainVisibleContentPosition(ReactScrollView view, ReadableMap value) { if (value != null) { From 306655a284c6a079e7fa52aa0b04851ab9c54991 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Mon, 3 Feb 2025 20:11:18 +0100 Subject: [PATCH 2/3] fix: match iOS behavior --- .../java/com/facebook/react/views/scroll/ReactScrollView.java | 2 ++ .../com/facebook/react/views/scroll/ReactScrollViewManager.java | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) 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 44657248aa7546..807391124f32e9 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 @@ -1130,6 +1130,8 @@ public void setContentInset(ReadableMap insets) { } setPadding(left, top, right, bottom); + // simulate iOS behavior + setClipToPadding(false); // If clipping of subviews is enabled, update the clipping rect if (mRemoveClippedSubviews) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java index 0d0f1465677aaa..0862c8e5083cd4 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java @@ -332,7 +332,6 @@ public void setContentInset(ReactScrollView view, ReadableMap value) { view.setContentInset(value); } - @ReactProp(name = "maintainVisibleContentPosition") public void setMaintainVisibleContentPosition(ReactScrollView view, ReadableMap value) { if (value != null) { From 0ee741306d1827b106798da04eb259d810932be3 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Fri, 7 Mar 2025 15:03:38 +0100 Subject: [PATCH 3/3] fix: include contentInset as validAttributes on Android now --- .../Components/ScrollView/ScrollViewNativeComponent.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js b/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js index 82f9c93baf348c..08a6fee827d4d2 100644 --- a/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js +++ b/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js @@ -39,6 +39,9 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = }, }, validAttributes: { + contentInset: { + diff: require('../../Utilities/differ/insetsDiffer').default, + }, contentOffset: { diff: require('../../Utilities/differ/pointsDiffer').default, },