Fix scrollview momentum not stopping on scrollTo/scrollToEnd for horizontal scrollviews (#39529)

Summary:
### Motivation
My main motivation for this is using nested horizontal `Flashlists` inside a vertical `Flashlist`.
Like a `RecyclerView`, since my horizontal lists get recycled, if I scroll say the first horizontal list and scroll down, then when this list gets recycled it continues scrolling even if the content is new:

![nested-list-recycling](https://github.com/facebook/react-native/assets/4534323/f75a2a9b-6ab5-4554-811f-8d85ddfb81de)

To handle this, I want to call `scrollTo` everytime a new row appears to reset the scroll offset, however I've realized this doesn't stop the scroll momentum

### The bug

When scrolling and calling `scrollTo`, scroll momentum should be stopped and we should scroll to where `scrollTo` asked for.
All credit goes to tomekzaw for https://github.com/facebook/react-native/pull/36104 who fixed it for vertical scrollviews

I realized we had the same issue for
- horizontal scroll views
- when calling `scrollToEnd`

| Vertical scrollview (working ) | Horizontal scrollview (before fix, not stopping ) | Horizontal scrollview (after fix )|
|--------|--------|--|
| ![vertical-scrolltoffset-working](https://github.com/facebook/react-native/assets/4534323/884d95ce-9b09-47aa-99a7-99ca579a8fc4)|![horizontal-scrolloffset-bug](https://github.com/facebook/react-native/assets/4534323/0065a9d0-f905-4354-9caa-29a0a1f914ba)|![horizontal-scrolloffset-fixed](https://github.com/facebook/react-native/assets/4534323/ab49c356-23e8-464a-83a9-c4632b9d673e)|

Based on  https://github.com/facebook/react-native/pull/38728 I kept all those calls to `abortAnimation` on the View Manager

## Changelog:

[ANDROID] [FIXED] - Fixed horizontal ScrollView momentum not stopping when calling scrollTo programmatically
[ANDROID] [FIXED] - Fixed ScrollView momentum not stopping when calling scrollToEnd programmatically

Pull Request resolved: https://github.com/facebook/react-native/pull/39529

Test Plan:
My test code is [this](https://gist.github.com/Almouro/3ffcfbda8e2239e64bee93985e243000)
Basically:
- a scrollview with a few elements
- some buttons to trigger a `scrollTo`

To reproduce the bug, I scroll then click one of the buttons triggering a `scrollTo`

I added `react-native@nightly` to my project, and copy pasted `packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll` folder from this branch to try out my fixes

Then tested the following scenarios on Android:

| List layout  | Method | Not animated | Animated|
|--------|--------|--|--|
| horizontal | scrollTo | ![horizontal-scrollTo-no](https://github.com/facebook/react-native/assets/4534323/bda87a19-43cf-4fe9-9340-f70a3d5cd6a4)| ![horizontal-scrollto-yes](https://github.com/facebook/react-native/assets/4534323/8628d7ff-ee28-4185-81d1-1783f0fd990f) |
| horizontal | scrollToEnd  |![horizontal-scrolltoend-no](https://github.com/facebook/react-native/assets/4534323/594bb539-cb27-4234-8a8f-74d5b2bbe25f) | ![horizontal-scrolltoend-yes](https://github.com/facebook/react-native/assets/4534323/7366abcd-95fb-42ab-89e1-38fd4b10d966)|
| vertical | scrollTo | ![vertical-scrollto-no](https://github.com/facebook/react-native/assets/4534323/54555250-d4df-4bc2-b20f-46c706e8c726)|![vertical-scrollto-yes](https://github.com/facebook/react-native/assets/4534323/0c109f81-0bbd-475b-90f1-f1467317c799) |
| vertical | scrollToEnd  | ![vertical-scrolltoend-no](https://github.com/facebook/react-native/assets/4534323/f48c8196-8f2f-4d98-b750-91c067d1a063) | ![vertical-scrolltoend-yes](https://github.com/facebook/react-native/assets/4534323/04bb06dc-7e20-40de-a40d-e1da1fec491a)|

Reviewed By: javache

Differential Revision: D49437886

Pulled By: ryancat

fbshipit-source-id: 358c9b0eed7dabcbc9b87a15d1a757b414ef514b
This commit is contained in:
almouro
2023-09-20 04:28:08 -07:00
committed by Facebook GitHub Bot
parent 196b3f9ef9
commit 2f86aafdfd
3 changed files with 9 additions and 0 deletions
@@ -231,6 +231,12 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
}
}
public void abortAnimation() {
if (mScroller != null && !mScroller.isFinished()) {
mScroller.abortAnimation();
}
}
public void setSnapInterval(int snapInterval) {
mSnapInterval = snapInterval;
}
@@ -199,6 +199,7 @@ public class ReactHorizontalScrollViewManager extends ViewGroupManager<ReactHori
@Override
public void scrollTo(
ReactHorizontalScrollView scrollView, ReactScrollViewCommandHelper.ScrollToCommandData data) {
scrollView.abortAnimation();
if (data.mAnimated) {
scrollView.reactSmoothScrollTo(data.mDestX, data.mDestY);
} else {
@@ -219,6 +220,7 @@ public class ReactHorizontalScrollViewManager extends ViewGroupManager<ReactHori
"scrollToEnd called on HorizontalScrollView without child");
}
int right = child.getWidth() + scrollView.getPaddingRight();
scrollView.abortAnimation();
if (data.mAnimated) {
scrollView.reactSmoothScrollTo(right, scrollView.getScrollY());
} else {
@@ -296,6 +296,7 @@ public class ReactScrollViewManager extends ViewGroupManager<ReactScrollView>
// ScrollView always has one child - the scrollable area
int bottom = child.getHeight() + scrollView.getPaddingBottom();
scrollView.abortAnimation();
if (data.mAnimated) {
scrollView.reactSmoothScrollTo(scrollView.getScrollX(), bottom);
} else {