Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.view.Gravity
import android.view.View
import android.view.ViewTreeObserver.OnPreDrawListener
import android.widget.EditText
import androidx.core.widget.NestedScrollView
import com.facebook.react.views.scroll.ReactScrollView
import com.facebook.react.views.textinput.ReactEditText
import com.reactnativekeyboardcontroller.log.Logger
Expand Down Expand Up @@ -105,7 +106,9 @@ val EditText.parentScrollViewTarget: Int
while (currentView != null) {
val parentView = currentView.parent as? View

if (parentView is ReactScrollView && parentView.scrollEnabled) {
if ((parentView is ReactScrollView && parentView.scrollEnabled) ||
parentView is NestedScrollView
) {
Comment on lines +109 to +111

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I like these changes, we miss one important thing - if ScrollView will be with disabled scroll then this code still return it as valid scrollable container.

Do you think we can add extension that can handle both cases?

import android.view.View
import com.facebook.react.views.scroll.ReactScrollView
import java.lang.reflect.Field
import java.lang.reflect.Method

internal fun View.isEnabledReactScrollView(): Boolean {
  if (this is ReactScrollView) {
    return scrollEnabled
  }

  if (ReactNestedScrollViewCompat.isInstance(this)) {
    return ReactNestedScrollViewCompat.isScrollEnabled(this)
  }

  return false
}

private object ReactNestedScrollViewCompat {
  private const val CLASS_NAME =
    "com.facebook.react.views.scroll.ReactNestedScrollView"

  private val clazz: Class<*>? by lazy(LazyThreadSafetyMode.PUBLICATION) {
    runCatching {
      Class.forName(
        CLASS_NAME,
        false,
        ReactScrollView::class.java.classLoader,
      )
    }.getOrNull()
  }

  private val scrollEnabledGetter: Method? by lazy(LazyThreadSafetyMode.PUBLICATION) {
    val c = clazz ?: return@lazy null

    runCatching {
      c.getMethod("getScrollEnabled")
    }.getOrNull()
      ?: runCatching {
        c.getMethod("isScrollEnabled")
      }.getOrNull()
  }

  private val scrollEnabledField: Field? by lazy(LazyThreadSafetyMode.PUBLICATION) {
    val c = clazz ?: return@lazy null

    runCatching {
      c.getDeclaredField("mScrollEnabled").apply {
        isAccessible = true
      }
    }.getOrNull()
  }

  fun isInstance(view: View): Boolean {
    return clazz?.isInstance(view) == true
  }

  fun isScrollEnabled(view: View): Boolean {
    scrollEnabledGetter?.let { getter ->
      return runCatching {
        getter.invoke(view) as Boolean
      }.getOrDefault(false)
    }

    scrollEnabledField?.let { field ->
      return runCatching {
        field.getBoolean(view)
      }.getOrDefault(false)
    }

    return false
  }
}

And use it like:

if (parentView.isEnabledReactScrollView()) {

I'm fine if you can simpler/better code. My main concern is that we need to check scrollEnabled property too.

Ideally would be also to check that it returns false with disabled ScrollView + check any possible performance impact. The reflection is an expensive thing and calling it for each view might be expensive 🤞

Let me know what do you think about it!

// If the parent is a vertical, scrollable ScrollView - return its id
return parentView.id
}
Expand Down
Loading