From e774c037bce40a4b48e78d2d0a1085a1e4f5a328 Mon Sep 17 00:00:00 2001 From: David Vacca Date: Wed, 29 Sep 2021 01:46:07 -0700 Subject: [PATCH] Implement snapToAlignment in vertical ScrollView Summary: This diff implements the SnapToAlignment functionality in ReactScrollView for RN Android. In order to use SnapToAlignment, the pagingEnabled prop should be set Based on the documentation the behavior implemented in this diff is more "advanced" than the one implemendted in RNiOS, because it let you snap without specifying any interval nor offset (it calculates the intervals in real time based on the size of its content) I still need to verify how different RNiOS and RN Android behaviors are changelog: [Android][Added] Implement SnapToAlignment in ReactScrollView Reviewed By: JoshuaGross Differential Revision: D31182786 fbshipit-source-id: a9b55d9c00326ae21ca9b89575e79c60bf9edcca --- .../scroll/ReactHorizontalScrollView.java | 2 +- .../react/views/scroll/ReactScrollView.java | 40 ++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 5d52faf1813..7f6e173f328 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -975,7 +975,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView itemStartOffset = item.getLeft() - (width - item.getWidth()); break; default: - throw new IllegalStateException(""); + throw new IllegalStateException("Invalid SnapToAlignment value: " + mSnapToAlignment); } if (itemStartOffset <= targetOffset) { if (targetOffset - itemStartOffset < targetOffset - smallerOffset) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java index 38ba923d613..06d928544b5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java @@ -7,7 +7,10 @@ package com.facebook.react.views.scroll; +import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_CENTER; import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_DISABLED; +import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_END; +import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_START; import android.animation.Animator; import android.animation.ObjectAnimator; @@ -641,6 +644,10 @@ public class ReactScrollView extends ScrollView return scroller.getFinalY(); } + private View getContentView() { + return getChildAt(0); + } + /** * This will smooth scroll us to the nearest snap offset point It currently just looks at where * the content is and slides to the nearest point. It is intended to be run after we are done @@ -697,7 +704,7 @@ public class ReactScrollView extends ScrollView } // pagingEnabled only allows snapping one interval at a time - if (mSnapInterval == 0 && mSnapOffsets == null) { + if (mSnapInterval == 0 && mSnapOffsets == null && mSnapToAlignment == SNAP_ALIGNMENT_DISABLED) { smoothScrollAndSnap(velocityY); return; } @@ -734,6 +741,37 @@ public class ReactScrollView extends ScrollView } } } + + } else if (mSnapToAlignment != SNAP_ALIGNMENT_DISABLED) { + ViewGroup contentView = (ViewGroup) getContentView(); + for (int i = 1; i < contentView.getChildCount(); i++) { + View item = contentView.getChildAt(i); + int itemStartOffset; + switch (mSnapToAlignment) { + case SNAP_ALIGNMENT_CENTER: + itemStartOffset = item.getTop() - (height - item.getHeight()) / 2; + break; + case SNAP_ALIGNMENT_START: + itemStartOffset = item.getTop(); + break; + case SNAP_ALIGNMENT_END: + itemStartOffset = item.getTop() - (height - item.getHeight()); + break; + default: + throw new IllegalStateException("Invalid SnapToAlignment value: " + mSnapToAlignment); + } + if (itemStartOffset <= targetOffset) { + if (targetOffset - itemStartOffset < targetOffset - smallerOffset) { + smallerOffset = itemStartOffset; + } + } + + if (itemStartOffset >= targetOffset) { + if (itemStartOffset - targetOffset < largerOffset - targetOffset) { + largerOffset = itemStartOffset; + } + } + } } else { double interval = (double) getSnapInterval(); double ratio = (double) targetOffset / interval;