facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
119.2k stars 24.33k forks source link

TextInput with textAlign='right' inside a ScrollView gets focused on scroll (Android only) #12167

Closed mmazzarolo closed 10 months ago

mmazzarolo commented 7 years ago

Description

Hello there, I've found a small issue on Android.
When a TextInput with has textAlign: 'right' inside a ScrollView and you press on it for scrolling it will focus instead (only on Android).
The correct behaviour would be to get focused only if you tap on it (without scrolling).
It works perfectly on iOS or if you remove the textAlign style.

EDIT: I'm having the same issue with textAlign: 'center'.
Works correctly with every other prop (left, justify, auto).

Reproduction

You can test it easily:

<ScrollView>
  <TextInput style={{ textAlign: 'right' }} />
  <View style={{ height: 2000 }} />
</ScrollView>

Additional Information

hoangpham95 commented 7 years ago

I just tested with react-native 0.40.0 and cannot reproduce the problem. Which Android version are you running in your devices? scroll

mmazzarolo commented 7 years ago

That's weird. Are you starting the scroll by tapping the textinput?
Also, is that a blank project?
I'm on 5.1.0.

Oh, and thank you for trying it man!

hoangpham95 commented 7 years ago

Yes, this is a blank project. I'm starting outside of the TextInput. If you tap the text input then it should be focused, so I think this might not be an issue.

mmazzarolo commented 7 years ago

Ah-ha, there it is!
Nope, it should not be focused because:

  1. If you remove the textAlign prop it doesn't get focused
  2. On iOS it doesn't get focused
  3. On many different native apps I tried it doesn't get focused (for example try the form in the new contact screen in Android)

Thank you anyway :)

hoangpham95 commented 7 years ago

You're right, I tried and saw the problem. The textAlign caused the text input to be focused on touch.

AndyJinSS commented 7 years ago

I have the same problem on react-native 0.45.1

igofind commented 7 years ago

+1 No one can solve this problem?

yangtenghuan commented 7 years ago

+1 multiline={true} keyboardType={"default"} and limit the maxLength can solve this problem temporarily @igofind

turlac commented 7 years ago

+1 @pecopeco just made my day. thank you for the workaround! Any idea how we can get around the keyboard limitation of it having to be 'default' and not 'numeric'?

igofind commented 7 years ago

@pecopeco Thanks ! It works!

kengjungwa commented 7 years ago

@pecopeco works but NOT on keyboardType={"numeric"}

ahmetacikgoz commented 7 years ago

+1 Same issue!

programmdesign commented 7 years ago

@pecopeco: thanks for the workaround. We can't use multiline though. Any idea for a workaround w/o multilines?

enamin commented 7 years ago

+1 Same Issue

yuvalsegev commented 7 years ago

+1

Anyone willing to help?

moughxyz commented 7 years ago

I solved this issue by installing a PanResponder:

  componentWillMount: function() {
    this._panResponder = PanResponder.create({
      // Ask to be the responder:
      onStartShouldSetPanResponder: (evt, gestureState) => true,
      onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
      onMoveShouldSetPanResponder: (evt, gestureState) => true,
      onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,

      onPanResponderGrant: (evt, gestureState) => {
        // The gesture has started. Show visual feedback so the user knows
        // what is happening!

        // gestureState.d{x,y} will be set to zero now
      },
      onPanResponderMove: (evt, gestureState) => {
        // The most recent move distance is gestureState.move{X,Y}

        // The accumulated gesture distance since becoming responder is
        // gestureState.d{x,y}
      },
      onPanResponderTerminationRequest: (evt, gestureState) => true,
      onPanResponderRelease: (evt, gestureState) => {
        // The user has released all touches while this view is the
        // responder. This typically means a gesture has succeeded
      },
      onPanResponderTerminate: (evt, gestureState) => {
        // Another component has become the responder, so this gesture
        // should be cancelled
      },
      onShouldBlockNativeResponder: (evt, gestureState) => {
        // Returns whether this component should block native components from becoming the JS
        // responder. Returns true by default. Is currently only supported on android.
        return true;
      },
    });
  },

Then on your main view:

 <View {...this._panResponder.panHandlers} />
nishiltamboli commented 7 years ago

@pecopeco Thanks mate that worked like a charm. Weird issue really.

moughxyz commented 7 years ago

For the record, The TextInput component is rendered inside of a TouchableWithoutFeedback that focuses on press, so I'm not sure how long any of these workarounds will last:

https://github.com/facebook/react-native/blob/master/Libraries/Components/TextInput/TextInput.js#L799

You might also be able to subclass TextInput, override the render method, and remove the Touchable wrapper.

derakhshanfar commented 7 years ago

same issue for me, my textInput is numeric and i don't want a multiLine textinput

derakhshanfar commented 7 years ago

@mobitar could you write example please?

aizigao commented 6 years ago

same problem, has some solution ?

derakhshanfar commented 6 years ago

any solution?!

yqz0203 commented 6 years ago

@derakhshanfar did you have resolved this problem?

louislatreille commented 6 years ago

+1 I also have the same issue when manually setting the height of the text input under the default height. This causes it to be vertically scrollable, and eat the ScrollView scroll action. Having the height set like that, removing the textAlign prop or the workaround proposed by @pecopeco aren't even working.

derakhshanfar commented 6 years ago

@yqz0203 according to this issue #15274 i think the problem is related to this but i couldn't fix it yet!

However, sometimes a parent will want to make sure that it becomes responder. This can be handled by using the capture phase. Before the responder system bubbles up from the deepest component, it will do a capture phase, firing on*ShouldSetResponderCapture. So if a parent View wants to prevent the child from becoming responder on a touch start, it should have a onStartShouldSetResponderCapture handler which returns true.

yanhaijing commented 6 years ago

I have the same problem on react-native 0.45.1

feong commented 6 years ago

I have the similar issue with RN 0.49.5 When there is a placeholder for a TextInput with AlignText = 'right', touch on it and move the finger away, the placeholder will disappeared.

Tankerxyz commented 6 years ago

Just wrap all your content in TouchableOpacity with activeOpacity={1} props.

thgala commented 6 years ago

Setup:

"react": "16.0.0",
"react-native": "0.49.3",

Number input component:

focus = () => {
  if (this.inputRef) {
    this.inputRef.focus();
  }
}

inputRef: ?TextInput;

assignRef = (inputRef: ?TextInput) => {
  if (inputRef) {
    this.inputRef = inputRef;
  }
}

render() {
  return (
    <TextInput
      keyboardType="numeric"
      ref={inputRef => this.assignRef(inputRef)}
      {...this.props}
    />
  );
}

Form component:

handleInputPress = () => {
  if (this.input) {
    this.input.focus();
  }
}

input: ?NumberInput;

assignNumberInputRef = (inputRef: ?NumberInput) => {
  if (inputRef) {
    this.input = inputRef;
  }
}

render() {
  return (
    <ScrollView>
      <TouchableOpacity activeOpacity={1} onPress={this.handleInputPress}>
        <View pointerEvents="none">
          <NumberInput
            ref={input => this.assignNumberInputRef(input)}
            textAlign="right"
            ...
          />
        </View>
      </TouchableOpacity>
    </ScrollView>
  )
}
SunSargent commented 6 years ago

@thgala can you show your NumberInput

thgala commented 6 years ago

@SunSargent it is right above the "Form component".

SunSargent commented 6 years ago

@thgala Thanks ! it work well.

gwuhaolin commented 6 years ago

same problem on react-native 0.54.3

yupeifeng commented 6 years ago

@thgala this method cause :I cannot move cursor and pastte

itskevinsam commented 6 years ago

ping..

Same issue on react-native 0.55.4

MujtabaFR commented 6 years ago

I believe that the issue is liked to this line in react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java But I couldn't fix it :( Hope someone will do so ..

fuchangge commented 6 years ago

@pecopeco Thanks ! It works!

mgolkardev commented 6 years ago

@thgala Thanks. It works!

internet5 commented 6 years ago

I have the same problem on react-native 0.57.4

Nagibaba commented 5 years ago

For the sake of easy describing, I emphasize again. as @thgala stated, try something like this: ` <TouchableOpacity onPress={()=>this.input.focus()} >

this.input = input} />

`

januslo commented 5 years ago

I have test and worked with:


import React, {Component} from 'react';
import {TextInput, View, TouchableOpacity} from 'react-native';

/**
 * 为修复当TextView 在ScrollView下,textAlign="right"时,Scrollview不能滚动的bug
 */
export default class TouchInput extends Component {

    input = null;
    handleInputPress = () => {
        if (this.input
            && (this.props.editable || this.props.editable == undefined)) {
            this.input.focus();
        }
    }

    assignNumberInputRef(input) {
        if (input) {
            this.input = input;
        }
    }

    render() {
        return <TouchableOpacity activeOpacity={1} onPress={this.handleInputPress} style={{flex: 1}}>
            <View pointerEvents="none" style={{flex: 1}}>
                <TextInput
                    ref={input => this.assignNumberInputRef(input)}
                    {...this.props}
                />
            </View>
        </TouchableOpacity>
    }
}

And use in the scrollview at a real project as:

 <ScrollView  keyboardShouldPersistTaps='always'>
                    <View style={[styles.ItemViewContainer,styles.ItemViewContainerMarginBottom]}>
                        <View style={styles.ItemViewContentSpecial}>
                            <TouchableOpacity style={styles.ItemViewUploadImg} onPress={()=>this.openCamera()}>
                                <Image source={{uri: Config.host +  product.ProductPic}} style={styles.uploadImg}/>
                            </TouchableOpacity>
                            <View style={styles.ItemViewUploadRight}>
                                <View style={[styles.ItemViewContent,styles.ItemViewContentBorder]}>
                                    <Text style={styles.ItemViewText}>货品名称</Text>
                                    <TouchInput
                                        style={styles.ItemViewRightText}
                                        underlineColorAndroid='transparent'
                                        placeholder='最多30个字符'
                                        value={product.ProductName}
                                        editable={this.state.isChecked}
                                        onChangeText={(t)=> {
                                            product.ProductName = t;
                                            this.changeStateValue({product: product})
                                        }}/>
                                </View>
                                <View style={styles.ItemView}>
                                    <TouchableOpacity style={styles.ItemRightView} disabled={!this.state.isChecked}
                                                      onPress={()=>this.goToProductType()}>
                                    <View style={styles.ItemViewContent}>
                                        <Text style={styles.ItemViewText}>货品类别</Text>
                                            <Text style={styles.ItemViewRightText}>{this.state.productType}</Text>
                                            <Image source={require('../../../images/arrow-right.png') } style={styles.ItemViewRightImg}/>
                                    </View>
                                    </TouchableOpacity>
                                </View>
                            </View>
                        </View>
       /** any codes continued **/

I suffer this at the production testing and make boss pissed off... thx @thgala so much, you save my life!

luohaoxin commented 5 years ago

@januslo it works well,thanks

cpojer commented 5 years ago

Is there anybody who could work on a pull request to React Native to fix this issue permanently?

italomlp commented 5 years ago

This bug still occurs in RN 0.57.4. I can't say for sure if occurs in 0.58+. Had someone found a workaround for it?

hussainahmad commented 5 years ago

still it is in 0.59.8 , not fixed yet

aliakbarazizi commented 5 years ago

Here is typescript version for @januslo solution

import React, { Component } from "react";
import {
  TextInput,
  TextInputProps,
  TouchableOpacity,
  View,
} from "react-native";

export default class TouchInput extends Component<TextInputProps> {
  input: TextInput | null = null;
  handleInputPress = () => {
    if (
      this.input &&
      (this.props.editable || this.props.editable == undefined)
    ) {
      this.input.focus();
    }
  }

  assignNumberInputRef(input: TextInput | null) {
    if (input) {
      this.input = input;
    }
  }

  render() {
    return (
      <TouchableOpacity
        activeOpacity={1}
        onPress={this.handleInputPress}
        style={{ flex: 1 }}
      >
        <View pointerEvents="none" style={{ flex: 1 }}>
          <TextInput
            ref={(input) => this.assignNumberInputRef(input)}
            {...this.props}
          />
        </View>
      </TouchableOpacity>
    );
  }
}
AdamGold commented 5 years ago

Any solution for this bug yet?

ammoradi commented 5 years ago

not fixed yet! still it is in 0.60.4

elmonshareh commented 5 years ago

I tried all the best way that solved my problem multiline={true} blurOnSubmit={true} (it prevents you to make a new line .) maxLength={20} I know that is a work around but that is the best way till now

leviduan commented 4 years ago

Thank you so much, you help me resolve my question.