Skip to content

Commit c616f35

Browse files
committed
Android: preserve NestedScrollView fling nested-scroll lifecycle
1 parent 40c0612 commit c616f35

2 files changed

Lines changed: 45 additions & 16 deletions

File tree

‎packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactNestedScrollView.kt‎

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<497c1f334f6b3325e032e09947f6fce4>>
7+
* @generated SignedSource<<a38f03b5fd0c32076a46efce475b5598>>
88
*/
99

1010
/**
@@ -706,21 +706,6 @@ constructor(context: Context, private val fpsListener: FpsListener? = null) :
706706

707707
if (pagingEnabled) {
708708
flingAndSnap(correctedVelocityY)
709-
} else if (scroller != null) {
710-
val scrollWindowHeight = height - paddingBottom - paddingTop
711-
scroller.fling(
712-
scrollX, // startX
713-
scrollY, // startY
714-
0, // velocityX
715-
correctedVelocityY, // velocityY
716-
0, // minX
717-
0, // maxX
718-
0, // minY
719-
Int.MAX_VALUE, // maxY
720-
0, // overX
721-
scrollWindowHeight / 2, // overY
722-
)
723-
postInvalidateOnAnimation()
724709
} else {
725710
super.fling(correctedVelocityY)
726711
}

‎packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/generate-nested-scroll-view.js‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,46 @@ function replaceCopyrightHeader(content, sourceFile) {
100100
return content.replace(COPYRIGHT_HEADER_PATTERN, generatedHeader(sourceFile));
101101
}
102102

103+
/**
104+
* Keep NestedScrollView in charge of the non-paging fling lifecycle.
105+
*
106+
* ReactScrollView intentionally drives its reflected OverScroller directly. For
107+
* the generated AndroidX variant, doing the same bypasses NestedScrollView.fling()
108+
* and therefore its TYPE_NON_TOUCH nested-scroll lifecycle.
109+
*/
110+
function replaceNestedScrollViewFling(content) {
111+
const scrollerFlingBranch = ` } else if (scroller != null) {
112+
val scrollWindowHeight = height - paddingBottom - paddingTop
113+
scroller.fling(
114+
scrollX, // startX
115+
scrollY, // startY
116+
0, // velocityX
117+
correctedVelocityY, // velocityY
118+
0, // minX
119+
0, // maxX
120+
0, // minY
121+
Int.MAX_VALUE, // maxY
122+
0, // overX
123+
scrollWindowHeight / 2, // overY
124+
)
125+
postInvalidateOnAnimation()
126+
} else {
127+
super.fling(correctedVelocityY)
128+
}`;
129+
const nestedScrollFlingBranch = ` } else {
130+
super.fling(correctedVelocityY)
131+
}`;
132+
const occurrenceCount = content.split(scrollerFlingBranch).length - 1;
133+
134+
if (occurrenceCount !== 1) {
135+
throw new Error(
136+
`Expected exactly one ReactScrollView fling scroller branch; found ${occurrenceCount}.`,
137+
);
138+
}
139+
140+
return content.replace(scrollerFlingBranch, nestedScrollFlingBranch);
141+
}
142+
103143
/**
104144
* Transform ReactScrollView.kt to ReactNestedScrollView.kt
105145
*/
@@ -121,6 +161,10 @@ function transformScrollView(content) {
121161
// Replace ReactScrollView with ReactNestedScrollView
122162
content = replaceClassNames(content);
123163

164+
// Unlike android.widget.ScrollView, AndroidX NestedScrollView owns the nested-scroll
165+
// lifecycle of a fling. Preserve that lifecycle in the generated variant.
166+
content = replaceNestedScrollViewFling(content);
167+
124168
// Make the class internal to keep it out of the public API
125169
content = content.replace(
126170
'public open class ReactNestedScrollView',

0 commit comments

Comments
 (0)