web-dev-qa-db-fra.com

Comment mettre au point automatiquement le prochain TextInput sur React-Native

J'essaie de créer un écran protégé par mot de passe. L'écran utilise 4 entrées numériques comme mot de passe.

La façon dont je fais cela est de créer un composant TextInput et de l'appeler 4 fois dans mon écran principal.

Le problème que j'ai est que les TextInputs ne se concentrent pas sur le suivant car je tape la valeur du TextInput précédent.

J'utilise des références pour tous les composants PasscodeTextInput (j'ai été informé qu'il s'agit d'une méthode héritée mais je ne connais pas d'autre moyen, hélas).

J'ai essayé cette méthode (sans créer mon propre composant), pas de chance aussi. MÉTHODE

Image

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 };

Mise à jour 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);
8
J.Doe

Vous ne pouvez pas transmettre le ref à <TextInput> En utilisant cette méthode car ref est l'un des accessoires spéciaux . Ainsi, appeler this.refs.passcode2 Vous renverra <PasscodeTextInput> À la place.

Essayez de modifier ce qui suit pour obtenir le ref de <TextInput>.

PasscodeTextInput.js

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

  ...

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

Ensuite, affectez le inputRef de <PasscodeTextInput> À une variable et utilisez focus() pour changer de focus (il n'est pas obsolète à partir de 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() empêche la mise au point lorsque vous essayez d'effacer l'ancien code d'accès et d'en saisir un nouveau.

6
max23_

Il existe un defaultProp pour TextInput où l'on peut se concentrer après le montage du composant.

autoFocus

Si true, concentre l'entrée sur componentDidMount, la valeur par défaut est false. pour plus d'informations, veuillez lire le Docs .

METTRE À JOUR

Après componentDidUpdate cela ne fonctionnera pas correctement. Dans ce cas, on peut utiliser ref pour se concentrer par programme.

11
Mukundhan

nous avons géré ce style d'écran avec une approche différente.

Plutôt que de gérer 4 TextInputs individuels et de gérer la navigation du focus à travers chacun (puis de revenir en arrière lorsque l'utilisateur supprime un caractère), nous avons un seul TextInput à l'écran mais invisible (c.-à-d. 0px x 0px) de large qui a le focus , configuration maxLength et clavier, etc.

Ce TextInput prend l'entrée de l'utilisateur mais ne peut pas réellement être vu, car chaque caractère est tapé, nous rendons le texte entré comme une simple série d'éléments View/Text, de style très similaire à votre écran ci-dessus.

Cette approche a bien fonctionné pour nous, sans avoir besoin de gérer ce à quoi le texte suivant ou le texte précédent doit se concentrer.

4
crafterm

Vous pouvez utiliser la méthode de mise au point onChangeText comme l'a déclaré Jason, en plus de l'ajout de maxLength={1} peut vous faire passer immédiatement à l'entrée suivante sans vérifier ce qui a été ajouté. (vient de remarquer que son est déconseillé , mais c'est toujours ainsi que j'ai résolu mon problème, et devrait bien fonctionner jusqu'à la v0.36, et ce lien explique comment mettre à jour la fonction obsolète).

  <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()
   }}
  />
  ...

Veuillez noter que mon utilisation des références est également obsolète, mais je viens de copier le code car je peux vous garantir que cela fonctionnait à l'époque (fonctionne avec optimisme maintenant aussi).

Enfin, le principal problème avec ce type d'implémentation est , une fois que vous essayez de supprimer un nombre avec retour arrière, votre focus passera au suivant, provoquant une grave UX problèmes. Cependant, vous pouvez écouter l'entrée de la touche de retour arrière et effectuer quelque chose de différent au lieu de vous concentrer sur l'entrée suivante. Je vais donc laisser un lien ici pour que vous puissiez approfondir si vous choisissez d'utiliser ce type d'implémentation.

Solution Hacky au problème décrit précédemment: Si vous vérifiez ce qui est entré dans onChangeText prop avant de faire quoi que ce soit, vous pouvez passer à l'entrée suivante si le valeur est un nombre , sinon (c'est un retour arrière), sautez en arrière. (Je viens juste de trouver cette idée, je ne l'ai pas essayée.)

2
eden

Je pense que le problème est que onSubmitEditing est lorsque vous appuyez sur la touche "retour" ou "entrée" du clavier normal ... il n'y a pas un de ces boutons sur le clavier.

En supposant que vous voulez que chaque entrée ait un seul caractère, vous pouvez regarder le onChangeText puis vérifier si le texte a une longueur 1 et appeler focus si la longueur est bien 1.

1
Jason Gaare
    <TextInput 
    ref={input => {
          this.nameOrId = input;
        }}
    />



    <TouchableOpacity
      onPress={()=>{
        this.nameOrId.focus()
      }}
     >
      <Text>Click</Text>
    </TouchableOpacity>
1
noe