Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Text in FlatList not selectable #37130

Closed
Abbondanzo opened this issue Apr 27, 2023 · 5 comments
Closed

[Android] Text in FlatList not selectable #37130

Abbondanzo opened this issue Apr 27, 2023 · 5 comments
Labels
Accessibility Component: FlatList Component: Text Needs: Triage 🔍 Newer Patch Available Platform: Android Android applications. Stale There has been a lack of activity on this issue and it may be closed soon.

Comments

@Abbondanzo
Copy link
Contributor

Abbondanzo commented Apr 27, 2023

Description

Summary

Text components rendered within the confines of a FlatList are not selectable even when the selectable property is set to true. Setting the removeClippedSubviews to false has a negative performance impact.

This bug can likely be attributed to the bug captured in Google's Issue Tracker here. I was able to override our RCTText manager and ReactTextView implementation with a subclass that performs the suggested fix here, and the views immediately became selectable.

Citations

Previous issues:
#12342
#14746
#26264
#27107

Previous fix:
#28952

Proposed Fix

Per the suggestion I linked above, a quick toggle of the textIsSelectable flag works just fine. Here's my implementation for a subclass in Kotlin:

internal class SelectableReactTextView(context: Context) : ReactTextView(context) {
  override fun onAttachedToWindow() {
    super.onAttachedToWindow()
    if (isTextSelectable) {
      setTextIsSelectable(false)
      setTextIsSelectable(true)
    }
  }
}

I propose that this toggle of the textIsSelectable property be added directly to the ReactTextView class in its onAttachedToWindow override. This fix is similar in spirit to the one in the "Previous fix" section above.

It's important to note that the textIsSelectable property is correctly set, alongside the focusable, focusableInTouchMode, clickable, and longClickable properties. The underlying recycler view simply does not honor these settings.

Underlying cause

The Editor class, responsible for enabling text selection in Android, during its prepareCursorControllers phase attempts to access the root view from the TextView. On normal text views (i.e. not mounted in a FlatList), the root view is a DecorView with layout parameters. On text views mounted by a FlatList, the root view is a ReactViewGroup with null layout parameters. Subsequently, the check to ensure that the window can handle selections fails and the Editor sets its internal mSelectionControllerEnabled property to false.

The Editor has a performLongClick handler where normal selection events would fire. Towards the tail end of the function's implementation, it tries to call selectCurrentWordAndStartDrag. The third check, checkField, returns false because its call to canSelectText returns false. The canSelectText function is checking for that aforementioned mSelectionControllerEnabled property. This is what causes the debug warning to fire "TextView does not support text selection. Selection cancelled."

So why does firing setTextIsSelectable first with false, then true, work? There's a check within every TextView that sees if it's internal reference to the Editor mEditor and the given selectable property match. Sure enough, they do! So the function returns early and doesn't execute mEditor.prepareCursorControllers. But by flipping the selectable property to false, we end up falling through the entire function to execute prepareCursorControllers. By this point, we know the view has properly been attached to a window and not to the ephemeral RecyclerView, so executing that function should re-enable the selection controller.

It's the same reason why the solution linked in the Google tracker uses setEnabled instead of setTextIsSelectable--that function also executes prepareCursorControllers. We could call setText too, or setMovementMode, but setTextIsSelectable felt like the leanest, safest way to do it.

React Native Version

0.70.4

Output of npx react-native info

We have a pretty custom setup where our NPM packages are managed internally, but here's my device:

System:
    OS: macOS 13.2.1
    CPU: (10) arm64 Apple M1 Max
    Memory: 339.73 MB / 64.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 19.5.0 - /opt/homebrew/bin/node
    Yarn: 1.22.19 - /opt/homebrew/bin/yarn
    npm: 9.3.1 - /opt/homebrew/bin/npm
    Watchman: 2023.04.10.00 - /opt/homebrew/bin/watchman
  Managers:
    CocoaPods: Not Found
  SDKs:
    iOS SDK:
      Platforms: DriverKit 22.4, iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4
    Android SDK: Not Found
  IDEs:
    Android Studio: 2022.2 AI-222.4459.24.2221.9862592
    Xcode: 14.3/14E222b - /usr/bin/xcodebuild
  Languages:
    Java: 11.0.16.1 - /usr/bin/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: Not Found
    react-native: 0.69.8 => 0.69.8 
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

Steps to reproduce

  1. Build a FlatList render item with a Text that has its selectable property set to true
  2. Try to select the text on Android, and fail with a warning: TextView does not support text selection. Selection cancelled.

Snack, code example, screenshot, or link to a repository

https://snack.expo.dev/@abbondanzo/react-native-android---text-not-selectable-in-flatlist

Here's a quick demo of the warnings that are thrown from the native platform using the code from the Snack linked above:
https://user-images.githubusercontent.com/10366495/234964124-1bbecae9-a6a0-48f7-9d34-858a5c5be460.mov

@github-actions
Copy link

⚠️ Newer Version of React Native is Available!
ℹ️ You are on a supported minor version, but it looks like there's a newer patch available. Please upgrade to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases.

@Abbondanzo
Copy link
Contributor Author

This issue is also present in 0.71.7

@github-actions
Copy link

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@github-actions github-actions bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Oct 26, 2023
Copy link

github-actions bot commented Nov 3, 2023

This issue was closed because it has been stalled for 7 days with no activity.

@github-actions github-actions bot closed this as completed Nov 3, 2023
@fabOnReact
Copy link
Contributor

fabOnReact commented Jan 16, 2024

@Abbondanzo Can you still reproduce it? I can publish the fix with https://github.com/fabriziobertoglio1987/react-native-improved

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Accessibility Component: FlatList Component: Text Needs: Triage 🔍 Newer Patch Available Platform: Android Android applications. Stale There has been a lack of activity on this issue and it may be closed soon.
Projects
None yet
Development

No branches or pull requests

3 participants