From b12256394e34c375942ca508ef79a8c816317976 Mon Sep 17 00:00:00 2001 From: David Vacca Date: Wed, 29 Sep 2021 01:46:07 -0700 Subject: [PATCH] Implement SnapToAlignment in ReactHorizontalScrollView Summary: This diff implements the SnapToAlignment functionality in ReactHorizontalScrollView 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 ReactHorizontalScrollView Reviewed By: JoshuaGross Differential Revision: D31174544 fbshipit-source-id: 204a82f55e3b7598124ce2528d8ad7d854c0ac77 --- .../scroll/ReactHorizontalScrollView.java | 36 ++++++++++++++++- .../ScrollView/ScrollViewSimpleExample.js | 39 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) 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 4ad2aede460501..5d52faf18130de 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 @@ -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; @@ -23,6 +26,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.widget.HorizontalScrollView; import android.widget.OverScroller; @@ -912,7 +916,7 @@ private void flingAndSnap(int velocityX) { } // pagingEnabled only allows snapping one interval at a time - if (mSnapInterval == 0 && mSnapOffsets == null) { + if (mSnapInterval == 0 && mSnapOffsets == null && mSnapToAlignment == SNAP_ALIGNMENT_DISABLED) { smoothScrollAndSnap(velocityX); return; } @@ -955,6 +959,36 @@ private void flingAndSnap(int velocityX) { } } } + } 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.getLeft() - (width - item.getWidth()) / 2; + break; + case SNAP_ALIGNMENT_START: + itemStartOffset = item.getLeft(); + break; + case SNAP_ALIGNMENT_END: + itemStartOffset = item.getLeft() - (width - item.getWidth()); + break; + default: + throw new IllegalStateException(""); + } + 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; diff --git a/packages/rn-tester/js/examples/ScrollView/ScrollViewSimpleExample.js b/packages/rn-tester/js/examples/ScrollView/ScrollViewSimpleExample.js index b4960f93ee4b15..312a4c42e53652 100644 --- a/packages/rn-tester/js/examples/ScrollView/ScrollViewSimpleExample.js +++ b/packages/rn-tester/js/examples/ScrollView/ScrollViewSimpleExample.js @@ -61,6 +61,45 @@ class ScrollViewSimpleExample extends React.Component<{...}> { ])} , ); + items.push( + + {this.makeItems(NUM_ITEMS, [ + styles.itemWrapper, + styles.horizontalItemWrapper, + styles.horizontalPagingItemWrapper, + ])} + , + ); + items.push( + + {this.makeItems(NUM_ITEMS, [ + styles.itemWrapper, + styles.horizontalItemWrapper, + styles.horizontalPagingItemWrapper, + ])} + , + ); + items.push( + + {this.makeItems(NUM_ITEMS, [ + styles.itemWrapper, + styles.horizontalItemWrapper, + styles.horizontalPagingItemWrapper, + ])} + , + ); const verticalScrollView = ( {items}