如何在组件安装时在React Native中自动打开键盘?

时间:2018-04-23 20:33:47

标签: react-native react-navigation

我的网页只有一个TextInput,我已通过autoFocus prop: autoFocus: true

<TextInput
   style={styles.textInput}
   placeholder="Quiz Deck Title"
   autoFocus={true}
   value={this.state.title}
   onChangeText={(title) => this.controlledTextInput(title)}
/>

我想避免的是要求用户在键盘弹出之前在TextInput框中“点击”。

但我喜欢自动打开软键盘(如果设备没有硬件键盘)。

有没有办法在反应原生中实现这一点?我正在为ios和android开发。

如果重要,我的页面将通过TabNavigator导航到 我之所以提到这一点是因为对另一个类似的SO问题的评论(见下文)表明,当他们使用StackNavigator到达他们的页面时,他们遇到了类似的问题。

注意类似的SO问题:
How to open keyboard automatically in React Native? :不提供解决方案,其他人的评论提出了与我相同的结果:输入是集中的,但键盘不会自动打开。
Close/hide the Android Soft Keyboard Android: show soft keyboard automatically when focus is on an EditText :正在使用原生的Android代码(java),而不是原生代码(javascript)。

注意:我正在开发使用Android模拟器Nexus 6P和Android 23(推荐)和ios模拟器与iPhone 6s,因为我没有物理设备。

修改:添加请求的代码

NewDeck.js (我想让键盘自动弹出的视图):

import React from 'react';
import { connect } from 'react-redux';
import { View, Text, TouchableOpacity,
         TextInput, KeyboardAvoidingView,
         StyleSheet, Platform,
       } from 'react-native';

import StyledButton from '../components/StyledButton';

import { saveDeck }      from '../store/decks/actionCreators';
import { getDeckList }   from '../store/decks/selectors';
import { fetchDecks }    from '../utils/api';
import { saveDeckTitle } from '../utils/api';
       } from '../utils/colors';
import { titleCase, stripInvalidChars, makeStringUnique }
  from '../utils/helpers';
import { white, gray, primaryColor, primaryColorDark, primaryColorLight,

class NewDeck extends React.Component {    
  state = {
    title: '',
    canSubmit: false,
  }    
  componentDidMount () {
    this.textInputRef.focus()
  }
  controlledTextInput(title){
    title = titleCase(stripInvalidChars(title));
    const canSubmit = this.isValidInput(title);
    this.setState({ title, canSubmit });
  }    
  isValidInput(text){
    return text.trim() !== '';
  }
  onBlur(){
    title = this.state.title.trim();
    const unique = makeStringUnique(title, this.props.existingTitles);
    this.setState({ title: unique });
  }    
  onSubmit(){
    let title = this.state.title.trim();    
    title = makeStringUnique(title, this.props.existingTitles)
    saveDeckTitle(title)    
    this.props.navigation.navigate('Home');
  }    
  render() {
      return (
          <View style={styles.container}>
            <View style={[styles.cardContainer, {flex: 1}]}>
              <Text  style={styles.instructionsText}
                >
                Title for your New Quiz Deck
              </Text>

              <KeyboardAvoidingView {...keyboardAvoidingViewProps}>
                <TextInput
                  style={styles.textInput}
                  placeholder="Quiz Deck Title"
                  value={this.state.title}
                  onChangeText={(title) => this.controlledTextInput(title)}
                  /* autoFocus={true} */
                  ref={ref => this.textInputRef = ref}
                  />
              </KeyboardAvoidingView>
            </View>

            <KeyboardAvoidingView
              {...keyboardAvoidingViewProps}
              style={[styles.buttonsContainer, styles.buttonContainer]}
              >
              <StyledButton
                style={[styles.item, style={flex: 2}]}
                onPress={() => this.onSubmit()}
                disabled={!this.state.canSubmit}
                >
                <Text>
                  Submit
                </Text>
              </StyledButton>
            </KeyboardAvoidingView>
          </View>
      );
  }
}

const keyboardAvoidingViewProps = {
  behavior: 'padding',
};

// ...styles definition here, - I posted it in a later code block, to minimize 
// clutter, in the event that it is irrelevant to this issue

function mapStoreToProps(store){
  const decks  = getDeckList(store) || null;
  // ensure titles are unique (better UX than if just make id unique)
  const existingTitles = decks && decks.map(deck => {
    return deck.title
  }) || [];
  return {
    existingTitles,
  }
}
export default connect(mapStoreToProps)(NewDeck);

TabNavigator StackNavigator 代码(在App.js中):

// ... the TabNavigator I'm using:
import { TabNavigator, StackNavigator } from 'react-navigation';

//... the class's render method, uses StackNavigator (as MainNavigation)
render(){
    return (
      <Provider store={createStore(rootReducer)}>
        <View style={{flex:1}}>
          <AppStatusBar
            backgroundColor={primaryColor}
            barStyle="light-content"
            />
          <MainNavigation />
        </View>
      </Provider>
    );
  }
}

// ... 
const Tabs = TabNavigator(
  {
    DeckList: {
      screen: DeckList,
      navigationOptions: {
        tabBarLabel: 'Quiz Decks',
        tabBarIcon: ({ tintColor }) =>  // icons only show in ios
          <Ionicons name='ios-bookmarks' size={30} color={tintColor} />
      },
    },
    NewDeck: {
      screen: NewDeck,
      navigationOptions: {
        tabBarLabel: 'Create New Deck',
        tabBarIcon: ({ tintColor }) => // icons only show in ios
          <FontAwesome name='plus-square' size={30} color={tintColor} />
      },
    },
  },
  {
    navigationOptions: {
      // do-not-display page headers for Tab Navigation
      header: null
    },    
    tabBarOptions: {
      // ios icon and text color; android text color
      activeTintColor:   Platform.OS === 'ios' ? primaryColor : white,    
      pressColor: white,    
      indicatorStyle: {
        backgroundColor: primaryColorDark,
        height: 3,
      },    
      style: {
        height: 56,
        backgroundColor: Platform.OS === 'ios' ? white  : primaryColor,    
        shadowColor: 'rgba(0, 0, 0, 0.24)',
        shadowOffset: {
          width: 0,
          height: 3
        },
        shadowRadius: 6,
        shadowOpacity: 1
      }
    }
  }
);

//... StackNavigator uses TabNavigator (as Tabs)
const stackScreenNavigationOptions = {
  headerTintColor:   white,
  headerStyle: {
    backgroundColor: primaryColor,
  }
};
const MainNavigation = StackNavigator(
  //  RouteConfigs: This is analogous to defining Routes in a web app
  {
    Home: {
      screen: Tabs,  // Which also loads the first Tab (DeckList)
    },
    Deck: {
      screen: Deck,
      navigationOptions: stackScreenNavigationOptions,
    },
    Quiz: {
      screen: Quiz,
      navigationOptions: stackScreenNavigationOptions,
    },
    NewDeck: {
      screen: NewDeck,
      navigationOptions: stackScreenNavigationOptions,
    },
    NewCard: {
      screen: NewCard,
      navigationOptions: stackScreenNavigationOptions,
    },
  },
);

这是 NewDeck.js

样式定义
const styles = StyleSheet.create({
  // CONTAINER styles
  wrapper: {
    // this was the previous container style
      flex: 1,
      backgroundColor: white,
      alignItems: 'center',
      justifyContent: 'center',
    },
  container: {
    flex: 1,
    backgroundColor: white,
    alignItems: 'center',
    justifyContent: 'space-between',

    padding: 10,
    paddingTop: 30,
    paddingBottom: 5,
  },
  cardContainer: {
    flex: 1,
    justifyContent: 'flex-start',
    alignSelf: 'stretch',
    backgroundColor: '#fefefe',

    padding:     20,
    marginLeft:  30,
    marginRight: 30,
    marginTop:   10,
    borderRadius: Platform.OS === 'ios' ? 20 : 10,

    shadowRadius: 3,
    shadowOpacity: 0.8,
    shadowColor: 'rgba(0, 0, 0, 0.24)',
    shadowOffset: {
      width: 0,
      height: 3,
    },
    marginBottom:20,
  },
  buttonsContainer: {
    flex: 3,
    alignSelf: 'stretch',
    justifyContent: 'flex-start',
  },
  buttonContainer: {
    justifyContent: 'center',
    margin: 10,
  },

  // TEXT Styles
  instructionsText: {
    flex: 1,
    fontSize: 20,
    color: gray,

    alignSelf: 'center',
    textAlign: 'center',
  },

  // INPUTTEXT styles
  textInput: {
    fontSize: 27,
    color: primaryColor,

    alignSelf: 'stretch',
    flexWrap:  'wrap',
    textAlign: 'center',
    marginTop: 10,
  },
});

StyledButton.js (基本上,TouchableOpacity具有特定于平台的样式,可在整个应用中普遍使用):

import React from 'react';
import { Text, TouchableOpacity, StyleSheet, Platform } from 'react-native';
import { white, gray, primaryColor, primaryColorLight, primaryColorDark} from '../utils/colors';

export default function TextButton({ children, onPress, customColor, disabled=false }) {
  const disabledColor = disabled ? gray : null;
  const backgroundColor = Platform.OS==='ios' ? white : disabledColor || customColor || primaryColorLight;
  const borderColor     = Platform.OS==='ios' ? disabledColor || customColor || primaryColorDark
  const textColor       = Platform.OS==='ios' ? disabledColor || customColor || primaryColor  : white;
  const btnStyle = Platform.OS==='ios' ? styles.iosBtn : styles.androidBtn;
  const txtStyle = styles.txtDefault;
  const btnColor = { backgroundColor, borderColor };
  const txtColor = { color: textColor };

  return (
      <TouchableOpacity
        onPress={onPress}
        disabled={disabled}
        style={[btnStyle, btnColor]}
        >
        <Text
          style={[styles.txtDefault, txtColor]}
          >
          {children}
        </Text>
      </TouchableOpacity>
  );
}

const styles = StyleSheet.create({
  txtDefault: {
    textAlign: 'center',
    // because of bleeding of white text to colored background on android,
    // enlarge text (or increase fontWeight) for better readability
    fontSize: Platform.OS==='ios' ? 15 : 18,
    padding: 10,
  },
  iosBtn: {
    height: 45,
    borderRadius: 7,
    alignSelf:      'center',
    justifyContent: 'center',
    alignItems:     'center',
    // ios only settings
    borderColor:   primaryColorDark,
    borderWidth:   1,
    borderRadius:  3,
    paddingLeft:  25,
    paddingRight: 25,
  },
  androidBtn: {
    height: 45,
    borderRadius: 5,
    alignSelf:      'center',
    justifyContent: 'center',
    alignItems:     'center',
    // android- only settings
    // (padding accepts clicks, vs. margin === no-click zone)
    padding: 20,
    paddingLeft:  15,
    paddingRight: 15,
  },
});

// ios has white buttons with colored outlines and colored text
// android has colored buttons with white text
// Pass in a button color, or it defaults to the App's primary colors

4 个答案:

答案 0 :(得分:3)

旧问题,但如果有人在这里搜索答案。

如果您被困在动画中(例如,来自另一个屏幕),看来键盘没有弹出。

您可以使用ref专注于您的输入,但必须包裹在InteractionManager.runAfterInteractions中才能使其正常工作。

这是我解决的方法:

export const CustomInput: FunctionComponent<Props & TextInputProps> = ({
  error,
  ...props
}) => {
  const inputRef = useRef<TextInput>(null);

  useEffect(() => {
    // Must run after animations for keyboard to automatically open
    InteractionManager.runAfterInteractions(() => {
      if (inputRef?.current) {
        inputRef.current.focus();
      }
    });
  }, [inputRef]);

  return (
    <View>
      <TextInput
        ref={inputRef}
        {...props}
      />
      {error && <ErrorText>{error}</ErrorText>}
    </View>
  );
};```

答案 1 :(得分:2)

只需将引用包装在超时内 setTimeout(()=>{this.textInputRef.focus()},100)

答案 2 :(得分:1)

当组件加载时,您可以随时在.focus 上使用TextInput,以便在您想要避开autoFocus时显示键盘。< / p>  

componentDidMount () {
    this.textInputRef.focus()
}

<TextInput
    style={styles.textInput}
    ref={ref => this.textInputRef = ref}
    placeholder="Quiz Deck Title"
    autoFocus={true}
    value={this.state.title}
    onChangeText={(title) => this.controlledTextInput(title)}
/>

正如docs

中所述
  

通过原生元素公开的两个方法是.focus().blur(),它们会以编程方式聚焦或模糊TextInput。

答案 3 :(得分:0)

我只需要“autoFocus”道具就可以完成今天的工作。

https://reactnative.dev/docs/textinput#autofocus