【问题标题】:How to autofocus next TextInput on react-native如何在 react-native 上自动聚焦下一个 TextInput
【发布时间】:2017-07-11 07:03:45
【问题描述】:

我正在尝试创建受密码保护的屏幕。屏幕将使用 4 位数字输入作为密码。

我这样做的方式是创建一个 TextInput 组件并在我的主屏幕中调用它 4 次。

我遇到的问题是,当我键入前一个 TextInput 的值时,TextInputs 不会专注于下一个。

我正在为所有 PasscodeTextInput 组件使用 refs(我被告知这是一种遗留方法,但我不知道任何其他方法,唉)。

试过这个方法(没有创建我自己的组件),也没有运气。 METHOD

index.ios.js

import React, { Component } from 'react';
import { AppRegistry, TextInput, View, Text } from 'react-native';
import { PasscodeTextInput } from './common';

export default class ProgressBar extends Component {
  render() {
    const { centerEverything, container, passcodeContainer,  textInputStyle} = styles;
    return (
      <View style={[centerEverything, container]}>
        <View style={[passcodeContainer]}>
          <PasscodeTextInput
            autoFocus={true}
            ref="passcode1"
            onSubmitEditing={(event) => { this.refs.passcode2.focus() }} />
          <PasscodeTextInput
            ref="passcode2"
            onSubmitEditing={(event) => { this.refs.passcode3.focus() }} />
          <PasscodeTextInput
            ref="passcode3"
            onSubmitEditing={(event) => { this.refs.passcode4.focus() }}/>
          <PasscodeTextInput
            ref="passcode4" />
        </View>
      </View>
    );
  }
}

const styles = {
  centerEverything: {
    justifyContent: 'center',
    alignItems: 'center',
  },
  container: {
    flex: 1,
    backgroundColor: '#E7DDD3',
  },
  passcodeContainer: {
    flexDirection: 'row',
  },
}

AppRegistry.registerComponent('ProgressBar', () => ProgressBar);

PasscodeTextInput.js

import React from 'react';
import {
  View,
  Text,
  TextInput,
  Dimensions
} from 'react-native';

const deviceWidth = require('Dimensions').get('window').width;
const deviceHeight = require('Dimensions').get('window').height;

const PasscodeTextInput = ({ ref, autoFocus, onSubmitEditing, onChangeText, value}) => {

  const { inputStyle, underlineStyle } = styles;

  return(
    <View>
      <TextInput
        ref={ref}
        autoFocus={autoFocus}
        onSubmitEditing={onSubmitEditing}
        style={[inputStyle]}
        maxLength={1}
        keyboardType="numeric"
        placeholderTextColor="#212121"
        secureTextEntry={true}
        onChangeText={onChangeText}
        value={value}
      />
      <View style={underlineStyle} />
    </View>
  );
}

const styles = {
  inputStyle: {
    height: 80,
    width: 60,
    fontSize: 50,
    color: '#212121',
    fontSize: 40,
    padding: 18,
    margin: 10,
    marginBottom: 0
  },
  underlineStyle: {
    width: 60,
    height: 4,
    backgroundColor: '#202020',
    marginLeft: 10
  }
}

export { PasscodeTextInput };

更新 1

index.ios.js

import React, { Component } from 'react';
import { AppRegistry, TextInput, View, Text } from 'react-native';
import { PasscodeTextInput } from './common';

export default class ProgressBar extends Component {

  constructor() {
    super()
    this.state = {
      autoFocus1: true,
      autoFocus2: false,
      autoFocus3: false,
      autoFocus4: false,
    }
  }

  onTextChanged(t) { //callback for immediate state change
    if (t == 2) { this.setState({ autoFocus1: false, autoFocus2: true }, () => { console.log(this.state) }) }
    if (t == 3) { this.setState({ autoFocus2: false, autoFocus3: true }, () => { console.log(this.state) }) }
    if (t == 4) { this.setState({ autoFocus3: false, autoFocus4: true }, () => { console.log(this.state) }) }
  }

  render() {
    const { centerEverything, container, passcodeContainer, testShit, textInputStyle } = styles;
    return (
      <View style={[centerEverything, container]}>
        <View style={[passcodeContainer]}>
          <PasscodeTextInput
            autoFocus={this.state.autoFocus1}
            onChangeText={() => this.onTextChanged(2)} />
          <PasscodeTextInput
            autoFocus={this.state.autoFocus2}
            onChangeText={() => this.onTextChanged(3)} />
          <PasscodeTextInput
            autoFocus={this.state.autoFocus3}
            onChangeText={() => this.onTextChanged(4)} />
          <PasscodeTextInput
            autoFocus={this.state.autoFocus4} />
        </View>
      </View>
    );
  }
}

const styles = {
  centerEverything: {
    justifyContent: 'center',
    alignItems: 'center',
  },
  container: {
    flex: 1,
    backgroundColor: '#E7DDD3',
  },
  passcodeContainer: {
    flexDirection: 'row',
  },
}

AppRegistry.registerComponent('ProgressBar', () => ProgressBar);

【问题讨论】:

  • stackoverflow.com/questions/32748718/… 这是信息。希望这会有所帮助。
  • @UjjwalNepal focus() 方法已弃用...,mitch 的答案在0.40 之后无法使用stackoverflow.com/a/41201939/5809351
  • 我想你可以完全避免在父级中存在状态,只需在componentDidMount 中关注第一个输入,你的onTextChanged 方法可能看起来像这样if t == 1 or 2 or 3 then focus the t+1'th input
  • @Igorsvee 如何做一个对componentDidMount的关注?在 componentDidMount 中管理状态令人沮丧,介意分享一些代码吗?
  • 当然,因为componentDidMount是在render方法之后调用的,这意味着元素已经被渲染,我们现在可以通过refs访问它们:this.refs.passcode1.focus()

标签: javascript facebook react-native textinput


【解决方案1】:

TextInput 有一个 defaultProp,可以在组件挂载后聚焦。

自动对焦

如果为 true,则将输入聚焦在 componentDidMount 上,默认值为 false。更多信息请阅读相关Docs

更新

componentDidUpdate 之后将无法正常工作。在这种情况下,可以使用ref 以编程方式聚焦。

【讨论】:

  • 你的回答是对的,但是componentDidUpdate之后就不能正常工作了。
  • 在这种情况下,可以使用 ref 以编程方式聚焦。只是陈述一个选项。
【解决方案2】:

您不能使用这种方式将ref 转发到&lt;TextInput&gt;,因为refspecial props 之一。因此,调用this.refs.passcode2 将返回&lt;PasscodeTextInput&gt;

尝试更改以下内容以从&lt;TextInput&gt; 获取ref

PasscodeTextInput.js

const PasscodeTextInput = ({ inputRef, ... }) => {

  ...

  return (
    <View>
      <TextInput
        ref={(r) => { inputRef && inputRef(r) }}
        ...
      />
    </View>
    ...
  );
}

然后,将&lt;PasscodeTextInput&gt; 中的inputRef 分配给一个变量,并使用focus() 切换焦点(从RN 0.41.2 开始不推荐使用它)。

index.ios.js

return (
  <PasscodeTextInput
    autoFocus={true}
    onChangeText={(event) => { event && this.passcode2.focus() }} />

  <PasscodeTextInput
    inputRef={(r) => { this.passcode2 = r }}
    onChangeText={(event) => { event && this.passcode3.focus() }} />

  <PasscodeTextInput
    inputRef={(r) => { this.passcode3 = r }}
    onChangeText={(event) => { event && this.passcode4.focus() }} />

  <PasscodeTextInput
    inputRef={(r) => { this.passcode4 = r }} />
);

P.S:event &amp;&amp; this.passcode2.focus() 防止在尝试清除旧密码并输入新密码时切换焦点。

【讨论】:

  • @J.doe :感谢代码。你删除删除键了吗?
  • 这救了我的命,而且比 forwardRef 等技术更简单。但我仍然希望 React Native 能够在未来原生处理焦点移动。
【解决方案3】:

我们用不同的方法处理这种风格的屏幕。

我们不是管理 4 个单独的 TextInput 并处理每个文本输入的焦点导航(然后当用户删除一个字符时再次返回),我们在屏幕上有一个 TextInput 但不可见(即 0px x 0px)宽有焦点、maxLength和键盘配置等。

这个 TextInput 接受用户的输入,但实际上看不到,因为每个字符都被输入,我们将输入的文本呈现为一系列简单的 View/Text 元素,其样式与上面的屏幕非常相似。

这种方法对我们来说效果很好,无需管理“下一个”或“上一个”TextInput 的焦点。

【讨论】:

  • 甜蜜的解决方案:)
【解决方案4】:

您可以像 Jason 所说的那样使用焦点方法 onChangeText,此外添加 maxLength={1} 可以让您立即跳转到下一个输入而不检查添加的内容。 (刚刚注意到它已弃用,但这仍然是我解决问题的方法,并且在 v0.36 之前应该可以正常工作,而这个 link 解释了您应该如何更新已弃用的函数)。

  <TextInput
   ref="first"
   style={styles.inputMini}
   maxLength={1}
   keyboardType="numeric"
   returnKeyType='next'
   blurOnSubmit={false}
   placeholderTextColor="gray"
   onChangeText={(val) => {
      this.refs['second'].focus()
   }}
  />
  <TextInput
   ref="second"
   style={styles.inputMini}
   maxLength={1}
   keyboardType="numeric"
   returnKeyType='next'
   blurOnSubmit={false}
   placeholderTextColor="gray"
   onChangeText={(val) => {
     this.refs['third'].focus()
   }}
  />
  ...

请注意,我对 refs 的使用也已被弃用,但我只是复制了代码,因为我可以向您保证当时可以使用(希望现在也可以使用)。

最后,这种实现的主要问题是,一旦您尝试使用退格键删除一个数字,您的焦点将跳转到下一个,从而导致严重的用户体验问题。但是,您可以侦听退格键输入并执行不同的操作,而不是专注于下一个输入。因此,我将在此处留下link 供您进一步调查您是否选择使用这种类型的实现。

先前描述的问题的破解解决方案:如果您在执行任何操作之前检查 onChangeText 属性中输入的内容,如果该值为 数字,则可以跳转到下一个输入,否则(这是一个退格),跳回来。 (刚想出这个主意,没试过。)

【讨论】:

    【解决方案5】:

    我认为问题在于onSubmitEditing 是当您按常规键盘上的“return”或“enter”键时...键盘上没有这些按钮。

    假设您希望每个输入只有一个字符,您可以查看onChangeText,然后检查文本长度是否为 1,如果长度确实为 1,则调用焦点。

    【讨论】:

    • 我是这么想的,我对我的代码做了一些更改,但它仍然无法正常工作
    【解决方案6】:
        <TextInput 
        ref={input => {
              this.nameOrId = input;
            }}
        />
    
    
    
        <TouchableOpacity
          onPress={()=>{
            this.nameOrId.focus()
          }}
         >
          <Text>Click</Text>
        </TouchableOpacity>
    

    【讨论】:

      【解决方案7】:

      我用这段代码解决: const VerifyCode: React.FC = ({ pass, onFinish }) => {

            const inputsRef = useRef<Input[] | null[]>([]);
            const [active, setActive] = useState<number>(0);
      
            const onKeyPress = ({ nativeEvent }: 
            NativeSyntheticEvent<TextInputKeyPressEventData>) => {
             if (nativeEvent.key === "Backspace") {
              if (active !== 0) {
                inputsRef.current[active - 1]?.focus();
                return setActive(active - 1);
              }
             } else {
                inputsRef.current[active + 1]?.focus();
                return setActive(active + 1);
             }
            return null;
           };
      
         return (
          <View style={styles.container}>
            <StyledInput
              onKeyPress={onKeyPress}
              autoFocus={active === 0}
              ref={(r) => {
                inputsRef.current[0] = r;
              }}
            />
            <StyledInput
              onKeyPress={onKeyPress}
              autoFocus={active === 1}
              ref={(r) => {
                inputsRef.current[1] = r;
              }}
            />
            <StyledInput
              onKeyPress={onKeyPress}
              autoFocus={active === 2}
              ref={(r) => {
                inputsRef.current[2] = r;
              }}
            />
            <StyledInput
              onKeyPress={onKeyPress}
              autoFocus={active === 3}
              ref={(r) => {
                inputsRef.current[3] = r;
              }}
            />
          </View>
       );
      };
      
      export default VerifyCode;
      

      我在所有输入中都放了一个 ref,当 onKeyPress 触发时,该函数会验证是否必须返回或转到下一个输入

      【讨论】:

      • 您好。尝试解释为什么您的解决方案有效。这样更容易理解。
      猜你喜欢
      • 1970-01-01
      • 2017-08-07
      • 2018-08-08
      • 2022-07-06
      • 2019-04-12
      • 2020-09-05
      • 1970-01-01
      • 1970-01-01
      • 2021-11-01
      相关资源
      最近更新 更多