Skip to content

Commit

Permalink
Fix non selectable Text in FlatList (#28952)
Browse files Browse the repository at this point in the history
Summary:
This issue fixes #26264 fixes #27107
Text is not selectable inside a FlatList on Android. The solution is to invalidate the ReactTextView after a change of the selectable prop. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future and make the Text selectable.

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[Android] [Fixed] - Fix non selectable Text in FlatList

Pull Request resolved: #28952

Test Plan:
**<details><summary>CLICK TO OPEN TESTS RESULTS</summary>**
<p>

The issue was demonstrated in the following [snack](https://snack.expo.io/fabrizio.bertoglio/selectable-bug-in-flatlist) (more info in issue #26264).

The solution is:

1) Calling `invalidate()` from [setSelectableText][1] after changing the `selectable` prop and `mSelectableText` value. [`invalidate()`](https://developer.android.com/reference/android/view/View#invalidate()) triggers the `onDraw` callback.

[1]: https://github.com/fabriziobertoglio1987/react-native/blob/8027524947cafd5cbdc492e4ef9c92b346fe23fc/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java#L427-L430

2) calling `setTextIsSelectable(mSelectableText);` from the [`onDraw`][2] callback

[2]: https://github.com/fabriziobertoglio1987/react-native/blob/8027524947cafd5cbdc492e4ef9c92b346fe23fc/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java#L456-L460

The example below is availabe in RNTester FlatList example. Two options (`onPressDisabled` and `textSelectable`) have been added to test the functionality inside a FlatList.

<p align="center">
<img src="https://user-images.githubusercontent.com/24992535/82672536-c2e74d80-9c40-11ea-8fd8-156bfacfac8a.gif" width="200" height="" />
</p>

</p>
</details>

Reviewed By: ShikaSD

Differential Revision: D30000870

Pulled By: lunaleaps

fbshipit-source-id: 4851a294960df0af057d006793aa9ba97c51e3f9
  • Loading branch information
fabOnReact authored and facebook-github-bot committed Sep 1, 2021
1 parent e2e3980 commit c360b1d
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
private boolean mAdjustsFontSizeToFit = false;
private int mLinkifyMaskType = 0;
private boolean mNotifyOnInlineViewLayout;
private boolean mTextIsSelectable = false;

private ReactViewBackgroundManager mReactBackgroundManager;
private Spannable mSpanned;
Expand Down Expand Up @@ -433,9 +434,16 @@ public void onStartTemporaryDetach() {
}
}

@Override
public void setTextIsSelectable(boolean selectable) {
mTextIsSelectable = selectable;
super.setTextIsSelectable(selectable);
}

@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
setTextIsSelectable(mTextIsSelectable);
if (mContainsImages && getText() instanceof Spanned) {
Spanned text = (Spanned) getText();
TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class);
Expand Down
4 changes: 3 additions & 1 deletion packages/rn-tester/js/components/ListExampleShared.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,14 @@ class ItemComponent extends React.PureComponent<{
onPress: (key: string) => void,
onShowUnderlay?: () => void,
onHideUnderlay?: () => void,
textSelectable?: ?boolean,
...
}> {
_onPress = () => {
this.props.onPress(this.props.item.key);
};
render(): React.Node {
const {fixedHeight, horizontal, item} = this.props;
const {fixedHeight, horizontal, item, textSelectable} = this.props;
const itemHash = Math.abs(hashCode(item.title));
const imgSource = THUMB_URLS[itemHash % THUMB_URLS.length];
return (
Expand All @@ -81,6 +82,7 @@ class ItemComponent extends React.PureComponent<{
{!item.noImage && <Image style={styles.thumb} source={imgSource} />}
<Text
style={styles.text}
selectable={textSelectable}
numberOfLines={horizontal || fixedHeight ? 3 : undefined}>
{item.title} - {item.text}
</Text>
Expand Down
23 changes: 22 additions & 1 deletion packages/rn-tester/js/examples/FlatList/FlatListExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ type State = {|
empty: boolean,
useFlatListItemComponent: boolean,
fadingEdgeLength: number,
onPressDisabled: boolean,
textSelectable: boolean,
|};

class FlatListExample extends React.PureComponent<Props, State> {
Expand All @@ -74,6 +76,8 @@ class FlatListExample extends React.PureComponent<Props, State> {
empty: false,
useFlatListItemComponent: false,
fadingEdgeLength: 0,
onPressDisabled: false,
textSelectable: true,
};

_onChangeFilterText = filterText => {
Expand Down Expand Up @@ -161,6 +165,16 @@ class FlatListExample extends React.PureComponent<Props, State> {
this.state.debug,
this._setBooleanValue('debug'),
)}
{renderSmallSwitchOption(
'onPress Disabled',
this.state.onPressDisabled,
this._setBooleanValue('onPressDisabled'),
)}
{renderSmallSwitchOption(
'Text Selectable',
this.state.textSelectable,
this._setBooleanValue('textSelectable'),
)}
{renderSmallSwitchOption(
'Use FlatListItemComponent',
this.state.useFlatListItemComponent,
Expand Down Expand Up @@ -236,6 +250,12 @@ class FlatListExample extends React.PureComponent<Props, State> {
data: state.data.concat(genItemData(100, state.data.length)),
}));
};
_onPressCallback = () => {
const {onPressDisabled} = this.state;
const warning = () => console.log('onPress disabled');
const onPressAction = onPressDisabled ? warning : this._pressItem;
return onPressAction;
};
_onRefresh = () => Alert.alert('onRefresh: nothing to refresh :P');
_renderItemComponent = () => {
const flatListPropKey = this.state.useFlatListItemComponent
Expand All @@ -253,9 +273,10 @@ class FlatListExample extends React.PureComponent<Props, State> {
item={item}
horizontal={this.state.horizontal}
fixedHeight={this.state.fixedHeight}
onPress={this._pressItem}
onPress={this._onPressCallback()}
onShowUnderlay={separators.highlight}
onHideUnderlay={separators.unhighlight}
textSelectable={this.state.textSelectable}
/>
);
},
Expand Down

0 comments on commit c360b1d

Please sign in to comment.