【问题标题】:How to open keyboard automatically in React Native when component mounts?组件挂载时如何在 React Native 中自动打开键盘?
【发布时间】:2018-10-03 23:55:34
【问题描述】:

我的页面只有一个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? :没有提供解决方案,其他人的 cmets 建议的结果与我相同:输入已聚焦,但键盘不会自动打开。
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);

TabNavigatorStackNavigator 代码(在 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

styles 定义
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

【问题讨论】:

  • 如果您只在模拟器上进行过测试,那么您可能只是默认隐藏了键盘。在ios模拟器中,按cmd + k 开启。
  • @MattAft - 是的,我确实碰巧找到了。很高兴您为未来的访问者提及它,因为这不是一个明显的要求。
  • @MattAft 目前在两个平台上,用户都需要在键盘出现之前“单击”文本字段。当我使用这样的应用程序时,我发现这是一个令人讨厌的功能。我更喜欢的是,当页面加载时键盘会自动弹出。
  • 您可以在组件加载时将焦点添加到任何textinput 以使用它的引用显示键盘
  • @MattAft 我错了:键盘不会在 Android 中自动弹出,但在 iOS 中会弹出(cmd-k 已打开)。

标签: react-native react-navigation


【解决方案1】:

只需将 ref 包裹在 timeout 内 setTimeout(()=&gt;{this.textInputRef.focus()},100)

【讨论】:

  • 你知道为什么需要这样做吗?
  • 我猜这个小小的延迟给了在调用 focus() 之前实际分配 ref 的时间。
【解决方案2】:

老问题,但如果有人在这里搜索答案..

如果您卡在动画中(例如,如果来自另一个屏幕),键盘似乎不会出现。

您可以使用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>
  );
};```

【讨论】:

    【解决方案3】:

    您始终可以在任何TextInput 上使用.focus当组件加载时,如果您想避免autoFocus,则显示键盘。

    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。

    【讨论】:

    • 不幸的是,这给了我同样的行为:用户必须在键盘弹出之前“点击”输入框。
    • 好的,当你加载同一个组件时,textInput 是焦点还是 unfocused
    • 嗯..好问题。光标未显示在 TextInput 框内(直到我在 TextInput 内单击),所以我怀疑它实际上不是“聚焦”。这对于您的代码和我发布的代码都是一样的。
    • 可能有一些overlay 或其他东西正在使用ref focus
    • 当你使用自动对焦时,它基本上会在组件安装后调用焦点,所以它是完全相同的东西。如果您键入并且它显示在 textinput 上,那么您就会知道它已获得焦点。
    【解决方案4】:

    我现在只需要“autoFocus”道具就可以做到这一点。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-02-08
      • 1970-01-01
      • 2016-11-11
      • 1970-01-01
      • 1970-01-01
      • 2019-01-07
      • 1970-01-01
      相关资源
      最近更新 更多