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
This commit is contained in:
David Vacca
2021-09-29 01:47:45 -07:00
committed by Facebook GitHub Bot
parent c6e5640e87
commit e774c037bc
2 changed files with 40 additions and 2 deletions
@@ -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) {
@@ -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;