From 2f86aafdfd42764b25d2e9bd68a05644965b89d7 Mon Sep 17 00:00:00 2001 From: almouro Date: Wed, 20 Sep 2023 04:28:08 -0700 Subject: [PATCH] Fix scrollview momentum not stopping on scrollTo/scrollToEnd for horizontal scrollviews (#39529) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../react/views/scroll/ReactHorizontalScrollView.java | 6 ++++++ .../views/scroll/ReactHorizontalScrollViewManager.java | 2 ++ .../facebook/react/views/scroll/ReactScrollViewManager.java | 1 + 3 files changed, 9 insertions(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index cfac976347ebed..7dbcb788c3ac32 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -231,6 +231,12 @@ public void setDecelerationRate(float decelerationRate) { } } + public void abortAnimation() { + if (mScroller != null && !mScroller.isFinished()) { + mScroller.abortAnimation(); + } + } + public void setSnapInterval(int snapInterval) { mSnapInterval = snapInterval; } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java index 85f784833b502a..d311a236ed96f7 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java @@ -199,6 +199,7 @@ public void flashScrollIndicators(ReactHorizontalScrollView scrollView) { @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 void scrollToEnd( "scrollToEnd called on HorizontalScrollView without child"); } int right = child.getWidth() + scrollView.getPaddingRight(); + scrollView.abortAnimation(); if (data.mAnimated) { scrollView.reactSmoothScrollTo(right, scrollView.getScrollY()); } else { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java index af022790a818b4..1b42aaefefcf92 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java @@ -296,6 +296,7 @@ public void scrollToEnd( // 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 {