Fix fling and snap in recycler viewgroup where children views not fill up all the scrollable space

Summary:
This diff fixes an edge case where fling and snap failed to find the correct target position when children views not fill up all the scrollable space. In this case, the target position would be calculated as the end of the scrollable space, which case the snap logic to go to the end of the scrollable area, instead of stop at the expected snapping position.

Changelog:
[Android][Fixed] - Fix fling and snap with recycler viewgroup where fling to the end of scrollable distance when it goes over current rendered children views.

Reviewed By: mdvacca

Differential Revision: D32565459

fbshipit-source-id: 319ef6e2d4e1c4deb9e45ed02c1bff7d807575c3
This commit is contained in:
Xin Chen
2021-12-06 09:40:57 -08:00
committed by Facebook GitHub Bot
parent 583471bc48
commit ead7b97944
2 changed files with 28 additions and 2 deletions
@@ -968,6 +968,8 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
maximumOffset);
} else {
ViewGroup contentView = (ViewGroup) getContentView();
int smallerChildOffset = largerOffset;
int largerChildOffset = smallerOffset;
for (int i = 0; i < contentView.getChildCount(); i++) {
View item = contentView.getChildAt(i);
int itemStartOffset =
@@ -983,7 +985,16 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
largerOffset = itemStartOffset;
}
}
smallerChildOffset = Math.min(smallerChildOffset, itemStartOffset);
largerChildOffset = Math.max(largerChildOffset, itemStartOffset);
}
// For Recycler ViewGroup, the maximumOffset can be much larger than the total heights of
// items in the layout. In this case snapping is not possible beyond the currently rendered
// children.
smallerOffset = Math.max(smallerOffset, smallerChildOffset);
largerOffset = Math.min(largerOffset, largerChildOffset);
}
} else {
double interval = getSnapInterval();
@@ -994,7 +1005,9 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
// Calculate the nearest offset
int nearestOffset =
targetOffset - smallerOffset < largerOffset - targetOffset ? smallerOffset : largerOffset;
Math.abs(targetOffset - smallerOffset) < Math.abs(largerOffset - targetOffset)
? smallerOffset
: largerOffset;
// if scrolling after the last snap offset and snapping to the
// end of the list is disabled, then we allow free scrolling
@@ -747,6 +747,8 @@ public class ReactScrollView extends ScrollView
maximumOffset);
} else {
ViewGroup contentView = (ViewGroup) getContentView();
int smallerChildOffset = largerOffset;
int largerChildOffset = smallerOffset;
for (int i = 0; i < contentView.getChildCount(); i++) {
View item = contentView.getChildAt(i);
int itemStartOffset;
@@ -774,7 +776,16 @@ public class ReactScrollView extends ScrollView
largerOffset = itemStartOffset;
}
}
smallerChildOffset = Math.min(smallerChildOffset, itemStartOffset);
largerChildOffset = Math.max(largerChildOffset, itemStartOffset);
}
// For Recycler ViewGroup, the maximumOffset can be much larger than the total heights of
// items in the layout. In this case snapping is not possible beyond the currently rendered
// children.
smallerOffset = Math.max(smallerOffset, smallerChildOffset);
largerOffset = Math.min(largerOffset, largerChildOffset);
}
} else {
double interval = (double) getSnapInterval();
@@ -785,7 +796,9 @@ public class ReactScrollView extends ScrollView
// Calculate the nearest offset
int nearestOffset =
targetOffset - smallerOffset < largerOffset - targetOffset ? smallerOffset : largerOffset;
Math.abs(targetOffset - smallerOffset) < Math.abs(largerOffset - targetOffset)
? smallerOffset
: largerOffset;
// if scrolling after the last snap offset and snapping to the
// end of the list is disabled, then we allow free scrolling