如何在 react-native 上自动聚焦下一个 TextInput
How to autofocus next TextInput on react-native
我正在尝试创建密码保护屏幕。屏幕将使用 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);
我认为问题是 onSubmitEditing
是当您按下常规键盘上的 "return" 或 "enter" 键时...键盘上没有这些按钮之一.
假设您希望每个输入只有一个字符,您可以查看 onChangeText
,然后检查文本的长度是否为 1,如果长度确实为 1,则调用 focus。
您可以使用焦点方法 onChangeText
正如 Jason 所说,此外添加 maxLength={1}
可以让您立即跳转到下一个输入而无需检查添加的内容。 (刚刚注意到它 deprecated,但这仍然是我解决问题的方法,并且在 v0.36 之前应该可以正常工作,并且这个 解释了你应该如何更新已弃用的函数).
<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 供您进一步调查。
先前描述的问题的 Hacky 解决方案: 如果你在做任何事情之前检查 onChangeText
prop 中输入的内容,如果值为 number, else(就是退格),往回跳。 (刚想出这个主意,没试过。)
您不能使用这种方式将 ref
转发给 <TextInput>
,因为 ref
是 special props 之一。因此,调用 this.refs.passcode2
将 return 你 <PasscodeTextInput>
。
尝试更改为以下内容以从 <TextInput>
.
获取 ref
PasscodeTextInput.js
const PasscodeTextInput = ({ inputRef, ... }) => {
...
return (
<View>
<TextInput
ref={(r) => { inputRef && inputRef(r) }}
...
/>
</View>
...
);
}
然后,将 <PasscodeTextInput>
中的 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 && this.passcode2.focus()
防止在尝试清除旧密码并输入新密码时切换焦点。
我们用不同的方法处理这种风格的屏幕。
我们没有管理 4 个单独的 TextInput 并处理每个 TextInput 的焦点导航(然后在用户删除字符时再次返回),我们在屏幕上有一个 TextInput 但不可见(即 0px x 0px) wide里面有focus, maxLength和keyboard configuration等
此 TextInput 从用户那里获取输入,但实际上是看不到的,因为每个字符都是在其中输入的,我们将输入的文本呈现为一系列简单的 View/Text 元素,其样式与上面的屏幕非常相似。
这种方法对我们来说效果很好,无需管理 'next' 或 'previous' TextInput 旁边的焦点。
TextInput 有一个 defaultProp,可以在组件安装后聚焦。
autoFocus
如果为true,则将输入聚焦于componentDidMount,默认值为false。欲了解更多信息,请阅读相关的 Docs.
更新
componentDidUpdate
后将无法正常工作。在这种情况下,可以使用 ref
以编程方式聚焦。
<TextInput
ref={input => {
this.nameOrId = input;
}}
/>
<TouchableOpacity
onPress={()=>{
this.nameOrId.focus()
}}
>
<Text>Click</Text>
</TouchableOpacity>
我用这段代码解决了:
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 触发时,函数验证是否必须返回或转到下一个输入
我正在尝试创建密码保护屏幕。屏幕将使用 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);
我认为问题是 onSubmitEditing
是当您按下常规键盘上的 "return" 或 "enter" 键时...键盘上没有这些按钮之一.
假设您希望每个输入只有一个字符,您可以查看 onChangeText
,然后检查文本的长度是否为 1,如果长度确实为 1,则调用 focus。
您可以使用焦点方法 onChangeText
正如 Jason 所说,此外添加 maxLength={1}
可以让您立即跳转到下一个输入而无需检查添加的内容。 (刚刚注意到它 deprecated,但这仍然是我解决问题的方法,并且在 v0.36 之前应该可以正常工作,并且这个
<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 供您进一步调查。
先前描述的问题的 Hacky 解决方案: 如果你在做任何事情之前检查 onChangeText
prop 中输入的内容,如果值为 number, else(就是退格),往回跳。 (刚想出这个主意,没试过。)
您不能使用这种方式将 ref
转发给 <TextInput>
,因为 ref
是 special props 之一。因此,调用 this.refs.passcode2
将 return 你 <PasscodeTextInput>
。
尝试更改为以下内容以从 <TextInput>
.
ref
PasscodeTextInput.js
const PasscodeTextInput = ({ inputRef, ... }) => {
...
return (
<View>
<TextInput
ref={(r) => { inputRef && inputRef(r) }}
...
/>
</View>
...
);
}
然后,将 <PasscodeTextInput>
中的 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 && this.passcode2.focus()
防止在尝试清除旧密码并输入新密码时切换焦点。
我们用不同的方法处理这种风格的屏幕。
我们没有管理 4 个单独的 TextInput 并处理每个 TextInput 的焦点导航(然后在用户删除字符时再次返回),我们在屏幕上有一个 TextInput 但不可见(即 0px x 0px) wide里面有focus, maxLength和keyboard configuration等
此 TextInput 从用户那里获取输入,但实际上是看不到的,因为每个字符都是在其中输入的,我们将输入的文本呈现为一系列简单的 View/Text 元素,其样式与上面的屏幕非常相似。
这种方法对我们来说效果很好,无需管理 'next' 或 'previous' TextInput 旁边的焦点。
TextInput 有一个 defaultProp,可以在组件安装后聚焦。
autoFocus
如果为true,则将输入聚焦于componentDidMount,默认值为false。欲了解更多信息,请阅读相关的 Docs.
更新
componentDidUpdate
后将无法正常工作。在这种情况下,可以使用 ref
以编程方式聚焦。
<TextInput
ref={input => {
this.nameOrId = input;
}}
/>
<TouchableOpacity
onPress={()=>{
this.nameOrId.focus()
}}
>
<Text>Click</Text>
</TouchableOpacity>
我用这段代码解决了: 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 触发时,函数验证是否必须返回或转到下一个输入