Skip to content

Commit cb7af02

Browse files
jasozhfacebook-github-bot
authored andcommitted
Fix TextInput absorbing scroll gestures on Android
Summary: Fixes the issue described in #25594 and #12167, where TextInputs on Android absorb scroll gestures when textAlign is set to "center" or "right". For TextInputs inside a ScrollView, starting a scroll gesture on the TextInput would not scroll the ScrollView. ### Root Cause The root cause is the `onTouchEvent()` function introduced in [commit 372d001](372d001). The smart scroll implementation checks if the TextInput can itself scroll in any direction. If it cannot, then the parent can intercept the gesture to apply normal scroll behavior. However, when textAlign is set to "center" or "right", `canScrollHorizontally()` returns true, as Android treats the TextInput as capable of scrolling left/right. In these cases, the existing conditional logic blocks all scroll gestures, even if a vertical swipe is applied. ### This PR Modify `ReactEditText` so that we only check to see if the TextInput can scroll in the direction of the provided touch gesture. If a TextInput can scroll horizontally but not vertically, vertical touch swipes will no longer interfere with the scroll gesture. Touch slop is also used to distinguish intentional swipe gestures from small micro-touch events. Changelog: [Android][Fixed] Prevent TextInput from preventing scroll gestures Differential Revision: D121519676
1 parent 5880e42 commit cb7af02

1 file changed

Lines changed: 28 additions & 9 deletions

File tree

  • packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput

‎packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt‎

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import android.view.Menu
3636
import android.view.MenuItem
3737
import android.view.MotionEvent
3838
import android.view.View
39+
import android.view.ViewConfiguration
3940
import android.view.ViewGroup
4041
import android.view.accessibility.AccessibilityNodeInfo
4142
import android.view.inputmethod.EditorInfo
@@ -91,6 +92,7 @@ import com.facebook.react.views.text.internal.span.ReactStrikethroughSpan
9192
import com.facebook.react.views.text.internal.span.ReactTextPaintHolderSpan
9293
import com.facebook.react.views.text.internal.span.ReactUnderlineSpan
9394
import java.util.concurrent.CopyOnWriteArrayList
95+
import kotlin.math.abs
9496
import kotlin.math.max
9597
import kotlin.math.min
9698

@@ -132,6 +134,9 @@ public open class ReactEditText public constructor(context: Context) : AppCompat
132134
private var scrollWatcher: ScrollWatcher?
133135
private var keyListener: InternalKeyListener? = null
134136
private var detectScrollMovement = false
137+
private var touchDownX = 0f
138+
private var touchDownY = 0f
139+
private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
135140
private var onKeyPress = false
136141
private val textAttributes: TextAttributes
137142
private var typefaceDirty = false
@@ -316,6 +321,8 @@ public open class ReactEditText public constructor(context: Context) : AppCompat
316321
override fun onTouchEvent(ev: MotionEvent): Boolean {
317322
when (ev.action) {
318323
MotionEvent.ACTION_DOWN -> {
324+
touchDownX = ev.x
325+
touchDownY = ev.y
319326
detectScrollMovement = true
320327
// Disallow parent views to intercept touch events, until we can detect if we should be
321328
// capturing these touches or not.
@@ -324,21 +331,33 @@ public open class ReactEditText public constructor(context: Context) : AppCompat
324331

325332
MotionEvent.ACTION_MOVE ->
326333
if (detectScrollMovement) {
327-
if (
328-
!canScrollVertically(-1) &&
329-
!canScrollVertically(1) &&
330-
!canScrollHorizontally(-1) &&
331-
!canScrollHorizontally(1)
332-
) {
333-
// We cannot scroll, let parent views take care of these touches.
334-
this.parent.requestDisallowInterceptTouchEvent(false)
334+
val deltaX = ev.x - touchDownX
335+
val deltaY = ev.y - touchDownY
336+
val dominantAxisDistance = max(abs(deltaX), abs(deltaY))
337+
if (dominantAxisDistance > touchSlop) {
338+
if (!canScrollInGestureDirection(deltaX, deltaY)) {
339+
// We cannot scroll, let parent views take care of these touches.
340+
this.parent.requestDisallowInterceptTouchEvent(false)
341+
}
342+
detectScrollMovement = false
335343
}
336-
detectScrollMovement = false
337344
}
338345
}
339346
return super.onTouchEvent(ev)
340347
}
341348

349+
private fun canScrollInGestureDirection(deltaX: Float, deltaY: Float): Boolean {
350+
if (abs(deltaY) > abs(deltaX)) {
351+
val canScrollUp = deltaY > 0 && canScrollVertically(-1)
352+
val canScrollDown = deltaY <= 0 && canScrollVertically(1)
353+
return canScrollUp || canScrollDown
354+
} else {
355+
val canScrollLeft = deltaX > 0 && canScrollHorizontally(-1)
356+
val canScrollRight = deltaX <= 0 && canScrollHorizontally(1)
357+
return canScrollLeft || canScrollRight
358+
}
359+
}
360+
342361
// Consume 'Enter' key events: TextView tries to give focus to the next TextInput, but it can't
343362
// since we only allow JS to change focus, which in turn causes TextView to crash.
344363
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {

0 commit comments

Comments
 (0)