React native 上一个文本输入自动对焦在backspace上按react原生android

React native 上一个文本输入自动对焦在backspace上按react原生android,react-native,React Native,我想将光标焦点移动到屏幕上以前的文本输入上。onkeyPress不起作用,因为该功能仅适用于IOS平台,不适用于Android。那么我如何在Android平台上防止这种方法呢 TextInput的示例代码: <TextInput style = {styles.input} keyboardType='numeric' maxLength={1} ref="otpcode1"

我想将光标焦点移动到屏幕上以前的文本输入上。onkeyPress不起作用,因为该功能仅适用于IOS平台,不适用于Android。那么我如何在Android平台上防止这种方法呢

TextInput的示例代码:

          <TextInput style = {styles.input}
            keyboardType='numeric'
            maxLength={1}
            ref="otpcode1"
            onChangeText={(event) => {
              this.setState({otp1: event});
              this.refs.otpcode2.focus() }}
            underlineColorAndroid='#fff'/>

          <TextInput style = {styles.input}
            keyboardType='numeric'
            maxLength={1}
            ref="otpcode2"
            onChangeText={(event) => {
              this.setState({otp2: event});
              this.refs.otpcode3.focus() }}
            underlineColorAndroid='#fff'/>

          <TextInput style = {styles.input}
            keyboardType='numeric'
            maxLength={1}
            ref="otpcode3"
            onChangeText={(event) => {
              this.setState({otp3: event});
              this.refs.otpcode4.focus() }}
            underlineColorAndroid='#fff'/>

         <TextInput style = {styles.input}
            keyboardType='numeric'
            maxLength={1}
            ref="otpcode4"
            onChangeText={(event) => {
              this.setState({otp4: event});
              console.log(this.state) }}
            underlineColorAndroid='#fff'/>
{
this.setState({otp1:event});
this.refs.otpcode2.focus()}
underlineColorAndroid='#fff'/>
{
this.setState({otp2:event});
this.refs.otpcode3.focus()}
underlineColorAndroid='#fff'/>
{
this.setState({otp3:event});
this.refs.otpcode4.focus()}
underlineColorAndroid='#fff'/>
{
this.setState({otp4:event});
console.log(this.state)}
underlineColorAndroid='#fff'/>

也有同样的问题,提出了一个肮脏的解决方案,但它是有效的。
用代码解释逻辑和问题

用法:

import {Alert} from 'react-native';

onPinEntered = (pin) => {
    Alert.alert(
      'Entered pin',
      pin,
      [{text: 'OK'}]
    )
}

render() {
    return (
        <PinInput onPinEntered={this.onPinEntered}/>
    )
}
从'react native'导入{Alert};
onPinEntered=(引脚)=>{
警惕,警惕(
“输入pin”,
别针,
[{文本:“确定”}]
)
}
render(){
返回(
)
}
组成部分:

import React, {PureComponent} from 'react';
import {
    Platform,
    StyleSheet,
    TextInput,
    View,
    Text,
    TouchableWithoutFeedback,
    Keyboard,
    Animated
} from 'react-native';
import PropTypes from 'prop-types';
// import ui_size from '../tools/UISizer';

let ui_size = function(points){
    //simple sizing function (it project it handles complex screen adaptation)
    return points*2
}

//Main technical problems:
//1. At date of development RN doesn't provide onKeyPress for android platform
//2. Changing text and selection of TextInput at same time is extremely buggy
//Solution:
//Create TextInput with maxLength = 2 and always prefilled with empty_character = ' ', (value={empty_character})
//add self-written cursor pointer
//track changes of text, if new text is '', backspace was pressed - move cursor to left by 1
//                       if new text is not empty, add new character to buffer (state.pin), mover cursor to right by 1
//Some times during reloads, selection is setting before defaultValue of TextInput and raises setSpan error (at least on Android)
//      so additionally track focused state (in render this.input.isFocused() raises strange error:
//                                           "TypeError owner.getName is not a function")
//On android, when input is focused and user hides keyboard by back button, it can only be shown by tap on input
//                    (.focus() don't show keyboard if input is already focused), so track it and blur the input

const empty_character = ' '

export default class PinInput extends PureComponent {

    static defaultProps = {
        maxChars: 4,
        inputProps: {},
    };

    static propTypes = {
        maxChars: PropTypes.number,
        onPinEntered: PropTypes.func,
        inputProps: PropTypes.object,
    };

    constructor(props) {
        super(props);
        this.state = {
            pin: '', // entered pin
            selection: undefined,  // cursor position
            isFocused: false,
            blinkAnim: new Animated.Value(1),
            isLastCharUpdated: false, // track if last character was updated
        }
    }

    onPressCell = (event, id) => {
        let {pin} = this.state
        // by pressing on unfilled cell, set cursor next to last filled cell
        if (id > pin.length) {
            id = pin.length
        }
        // set cursor position
        this.setState({
            selection: id,
        })
        this.input.focus()
    }

    cycleBlinkAnimation = () => {
        Animated.sequence([
            Animated.timing(this.state.blinkAnim, {
                toValue: 0,
                duration: 1000
            }),
            Animated.timing(this.state.blinkAnim, {
                toValue: 1,
                duration: 50
            }),
            Animated.timing(this.state.blinkAnim, {
                toValue: 1,
                duration: 300
            })
        ]).start((event) => {
            if (event.finished && this.state.isFocused) {
                this.cycleBlinkAnimation()
            }
        })
    }

    onFocus = (event) => {
        console.log('onFocus')
        let {selection} = this.state
        // update cursor position only if it wasn't setted up
        if (selection === undefined) {
            selection = 0
        }

        this.setState({
            selection,
            isFocused: true,
        })
    }

    onBlur = (event) => {
        console.log('onBlur')
        this.setState({
            selection: undefined,
            isFocused: false,
            isLastCharUpdated: false,
        })
    }

    componentWillUpdate(nextProps, nextState) {
        if (this.state.isFocused && !nextState.isFocused) {
            this.state.blinkAnim.stopAnimation()
        }
        //restart animation on focus or when cursor moved
        if ((this.state.selection !== nextState.selection || !this.state.isFocused) && nextState.isFocused) {
            this.state.blinkAnim.stopAnimation(() => {
                this.state.blinkAnim.setValue(1)
                this.cycleBlinkAnimation()
            })
        }
    }

    componentDidUpdate(prevProps, prevState) {
        if (this.state.isFocused
            && this.state.pin.length === this.props.maxChars  // input is full
            && this.state.selection+1 === this.props.maxChars // cursor is in last cell
            // && prevState.pin[3] !== this.state.pin[3]) { // last cell was changed
            && this.state.isLastCharUpdated) {
            console.log('blur componentDidUpdate')
            this.input.blur()
            setTimeout(this.onPinEntered, 1) // dirty hack, on ios sync call onPinEntered prevents blur
        }
    }

    componentWillMount() {
        this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this.keyboardDidHide);
    }

    componentDidMount() {
        this.cycleBlinkAnimation()
    }

    componentWillUnmount() {
        this.keyboardDidHideListener.remove()
    }

    keyboardDidHide = () => {
        // see reason in top
        // to prevent unfocussing in IOS simulator with connected hardware keyboard uncomment the following line
        //  or disconnect hardware keyboard by unchecking simulator > Hardware > Keyboard > Connect Hardware Keyboard
        // if (Platform.OS === 'ios') return;
        if (this.state.isFocused) {
            this.input.blur()
        }
    }

    onChangeText = (text) => {
        let {pin, selection} = this.state
        text = text.replace(empty_character, '') //remove first occurrence of empty_character
        let str_replaceAt = function (string, index, replacement) {
            return string.substr(0, index) + replacement + string.substr(index + 1);
        }
        let isLastCharUpdated = false
        if (text.length === 0) { //backspace
            pin = str_replaceAt(pin, selection, '')
            selection -= 1
            selection = Math.max(selection, 0)
        } else { //character entered
            pin = str_replaceAt(pin, selection, text)
            selection += 1
            if (selection >= this.props.maxChars) {
                isLastCharUpdated = true
            }
            selection = Math.min(selection, this.props.maxChars - 1)
        }
        this.setState({pin, selection, isLastCharUpdated})
    }

    onPinEntered = () => {
        if ('function' === typeof this.props.onPinEntered) {
            this.props.onPinEntered(this.state.pin)
        }
    }

    render_cell(id, value, is_active) {
        let style = (is_active) ? [styles.cell, styles.active_cell] : styles.cell
        let animation_style =
            [styles.cursor, {
                opacity: this.state.blinkAnim,         // Bind opacity to animated value
            }]
        return (
            <TouchableWithoutFeedback key={id} onPress={(event) => this.onPressCell(event, id)}>
                <View>
                    <Text style={style}>{value}</Text>
                    { is_active && <Animated.View style={animation_style} />}
                </View>
            </TouchableWithoutFeedback>
        )
    }

    render() {
        let inputs = []
        let {pin, selection} = this.state

        //render cells
        for (let i = 0; i < this.props.maxChars; i++) {
            inputs.push(this.render_cell(i, pin[i], (selection === i)))
        }

        // place cursor after empty_character
        let input_selection = undefined
        // as described in top: Some times during reloads, selection is setting before defaultValue ...
        if (this.input !== undefined && this.state.isFocused) {
            // so set selection after first character (empty_character) only when input is focused
            input_selection = {start: 1, end: 1}
        }

        let root_style = [this.props.style || {}, styles.root]

        return (
            <View style={root_style}>
                <TextInput
                           style={styles.actual_input}
                           ref={(input) => this.input = input}
                           maxLength={2}
                           selection={input_selection}
                           onChangeText={this.onChangeText}
                           autoComplete={false}
                           autoCorrect={false}
                           defaultValue={empty_character}
                           value={empty_character}
                           onFocus={this.onFocus}
                           onBlur={this.onBlur}
                           onSubmitEditing={this.onPinEntered}
                           {...this.props.inputProps}
                />
                <View style={styles.cells_wrapper}>
                    {inputs}
                </View>
            </View>
        )
    }
}

const styles = StyleSheet.create({
    root: {},
    actual_input: {
        position: 'absolute',
        display: 'none',
    },
    cells_wrapper: {
        flexDirection: 'row',
    },
    cell: {
        height: ui_size(24.8),
        width: ui_size(19.2),
        textAlign: 'center',
        elevation: 10,
        fontSize: ui_size(18),
        lineHeight: ui_size(24),
        backgroundColor: 'white',
        color: 'black',
        fontWeight: 'bold',
        paddingHorizontal: ui_size(3),
        margin: ui_size(6.7) / 2,
        borderRadius: ui_size(2.5),
        borderBottomColor: 'transparent',
        zIndex: 1,
        overflow: 'hidden', // crop corners in IOS
    },
    active_cell: {
        color: 'rgba(0,0,0,0.4)',
    },
    cursor: {
        position: 'absolute',
        height: 1,
        width: '50%',
        left: '25%',
        bottom: 12,
        borderBottomColor: 'rgba(0,0,0,0.5)',
        borderBottomWidth: 1,
        zIndex: 2,
        elevation: 10,
    }
});
import React,{PureComponent}来自'React';
进口{
平台,
样式表,
文本输入,
看法
文本,
可触摸且无反馈,
键盘
有生气的
}从“反应本机”;
从“道具类型”导入道具类型;
//从“../tools/UISizer”导入ui_大小;
让ui_大小=函数(点){
//简单的大小调整功能(it项目处理复杂的屏幕调整)
返回点*2
}
//主要技术问题:
//1. 在开发之日,RN没有为android平台提供onKeyPress
//2. 同时更改文本和选择文本输入是非常错误的
//解决方案:
//创建maxLength=2的TextInput,并始终以空字符=“”(值={empty\u character})作为前缀
//添加自写光标指针
//跟踪文本的更改,如果新文本为“”,则按backspace-将光标向左移动1
//若新文本不是空的,则将新字符添加到缓冲区(state.pin),将光标向右移动1
//在重新加载期间的某些时候,选择在TextInput的defaultValue之前设置,并引发setSpan错误(至少在Android上)
//因此,另外跟踪聚焦状态(在render this.input.isFocused()中)会引发奇怪的错误:
//“TypeError owner.getName不是函数”)
//在android上,当输入被聚焦并且用户通过后退按钮隐藏键盘时,它只能通过点击输入来显示
//(.focus()如果输入已经聚焦,则不显示键盘),因此跟踪它并模糊输入
常量空字符=“”
导出默认类PinInput扩展PureComponent{
静态defaultProps={
maxChars:4,
inputProps:{},
};
静态类型={
maxChars:PropTypes.number,
onPinEntered:PropTypes.func,
inputProps:PropTypes.object,
};
建造师(道具){
超级(道具);
此.state={
pin:“”,//输入了pin
选择:未定义,//光标位置
isFocused:错,
blinkAnim:新的动画值(1),
isLastCharUpdated:false,//跟踪是否更新了最后一个字符
}
}
onPressCell=(事件,id)=>{
设{pin}=this.state
//按下未填充的单元格,将光标设置在最后一个填充的单元格旁边
如果(id>引脚长度){
id=销长度
}
//设置光标位置
这是我的国家({
选择:id,
})
this.input.focus()
}
cycleBlinkAnimation=()=>{
动画序列([
动画。计时(this.state.blinkAnim{
toValue:0,
持续时间:1000
}),
动画。计时(this.state.blinkAnim{
toValue:1,
持续时间:50
}),
动画。计时(this.state.blinkAnim{
toValue:1,
持续时间:300
})
]).start((事件)=>{
if(event.finished&&this.state.isFocused){
这个文件名为.cycleBlinkAnimation()
}
})
}
onFocus=(事件)=>{
console.log('onFocus')
设{selection}=this.state
//仅在未设置光标位置时更新光标位置
如果(选择===未定义){
选择=0
}
这是我的国家({
选择,
是的,
})
}
onBlur=(事件)=>{
console.log('onBlur')
这是我的国家({
选择:未定义,
isFocused:错,
isLastCharUpdated:false,
})
}
组件将更新(下一步,下一步状态){
if(this.state.isFocused&!nextState.isFocused){
this.state.blinkAnim.stopAnimation()
}
//在焦点或光标移动时重新启动动画
if((this.state.selection!==nextState.selection | | |!this.state.isFocused)&&nextState.isFocused){
this.state.blinkAnim.stopAnimation(()=>{
this.state.blinkAnim.setValue(1)
这个文件名为.cycleBlinkAnimation()
})
}
}
componentDidUpdate(prevProps、prevState){
如果(this.state.isFocused
&&this.state.pin.length==this.props.maxChars//输入已满
&&this.state.selection+1===this.props.maxChars//光标位于最后一个单元格中
//&&prevState.pin[3]!==this.state.pin[3]){//最后一个单元格已更改
&&this.state.isLastChar(已更新){
console.log('blur componentdiddupdate')
this.input.blur()
setTimeout(this.onPinEntered,1)//dirty hack,on-ios同步调用onPinEntered防止模糊
}
}
组件willmount(){
class Test extends Component {

    constructor() {
        super();

        this.handleKeyPress = this.handleKeyPress.bind(this);
    }

    render() {
        return (
            <TextInput
                onKeyPress={ this.handleKeyPress }
            />
        )
    }

    handleKeyPress({ nativeEvent: { key: keyValue } }) {
        if (keyValue === 'Backspace') {
            this.refs.refOfPreviousInput.focus();
        }
    }
}
class Test extends Component {

    constructor() {
        super();

        this.handleKeyPress = this.handleKeyPress.bind(this);
    }

    render() {
        return (
            <TextInput
                onChangeText={ this.handleChangeText }
            />
        )
    }

    handleChangeText(value) {
        if (value.length === 0) {
            this.refs.refOfPreviousInput.focus();
        }
    }
}