当TextInput具有焦点时,如何从键盘后面自动滑出窗口?

我已经看到了本机应用程序自动滚动窗口,但想知道最好的方式来做到这一点反应本地…当一个领域获得焦点,并被定位在视图低,键盘将掩盖文本字段。 您可以在示例UIExplorer的TextInputExample.js视图中看到此问题。 任何一个有一个好的解决scheme?

2017答案

KeyboardAvoidingView可能是现在最好的方法。 看看这里的文档。

2015答案

react-native中执行此操作的正确方法不需要外部库,可以利用本地代码并包含animation。

首先定义一个函数来处理每个TextInput (或者你想滚动到的任何其他组件)的onFocus事件:

 // Scroll a component into view. Just pass the component ref string. inputFocused (refName) { setTimeout(() => { let scrollResponder = this.refs.scrollView.getScrollResponder(); scrollResponder.scrollResponderScrollNativeHandleToKeyboard( React.findNodeHandle(this.refs[refName]), 110, //additionalOffset true ); }, 50); } 

然后,在你的渲染函数中:

 render () { return ( <ScrollView ref='scrollView'> <TextInput ref='username' onFocus={this.inputFocused.bind(this, 'username')} </ScrollView> ) } 

这使用RCTDeviceEventEmitter来处理键盘事件和大小,使用RCTUIManager.measureLayout测量组件的位置,并计算scrollResponderInputMeasureAndScrollToKeyboard所需的确切的滚动移动。

您可能想要使用additionalOffset参数来适应特定UIdevise的需求。

Facebook 开源的 KeyboardAvoidingView反应原生0.29来解决这个问题。 文档和使用示例可以在这里find。

我们结合了一些代码formsreact-native-keyboard-spacer和来自@Sherlock的代码来创build一个KeyboardHandler组件,它可以用任何包含TextInput元素的View来包装。 奇迹般有效! 🙂

 /** * Handle resizing enclosed View and scrolling to input * Usage: * <KeyboardHandler ref='kh' offset={50}> * <View> * ... * <TextInput ref='username' * onFocus={()=>this.refs.kh.inputFocused(this,'username')}/> * ... * </View> * </KeyboardHandler> * * offset is optional and defaults to 34 * Any other specified props will be passed on to ScrollView */ 'use strict'; var React=require('react-native'); var { ScrollView, View, DeviceEventEmitter, }=React; var myprops={ offset:34, } var KeyboardHandler=React.createClass({ propTypes:{ offset: React.PropTypes.number, }, getDefaultProps(){ return myprops; }, getInitialState(){ DeviceEventEmitter.addListener('keyboardDidShow',(frames)=>{ if (!frames.endCoordinates) return; this.setState({keyboardSpace: frames.endCoordinates.height}); }); DeviceEventEmitter.addListener('keyboardWillHide',(frames)=>{ this.setState({keyboardSpace:0}); }); this.scrollviewProps={ automaticallyAdjustContentInsets:true, scrollEventThrottle:200, }; // pass on any props we don't own to ScrollView Object.keys(this.props).filter((n)=>{return n!='children'}) .forEach((e)=>{if(!myprops[e])this.scrollviewProps[e]=this.props[e]}); return { keyboardSpace:0, }; }, render(){ return ( <ScrollView ref='scrollView' {...this.scrollviewProps}> {this.props.children} <View style={{height:this.state.keyboardSpace}}></View> </ScrollView> ); }, inputFocused(_this,refName){ setTimeout(()=>{ let scrollResponder=this.refs.scrollView.getScrollResponder(); scrollResponder.scrollResponderScrollNativeHandleToKeyboard( React.findNodeHandle(_this.refs[refName]), this.props.offset, //additionalOffset true ); }, 50); } }) // KeyboardHandler module.exports=KeyboardHandler; 

首先你需要安装react-native-keyboardevents 。

  1. 在XCode中,在项目导航器中,右键单击库➜添加文件到[您的项目名称]转到node_modules➜react-native-keyboardevents并添加.xcodeproj文件
  2. 在XCode中,在项目导航器中select您的项目。 将keyboardevents项目中的lib * .a添加到项目的构build阶段➜链接使用库的二进制文件在项目导航器中单击之前添加的.xcodeproj文件,然后转到“构build设置”选项卡。 确保“全部”开启(而不是“基本”)。 查找标题searchpath并确保它包含$(SRCROOT)/../ react-native / React和$(SRCROOT)/../../ React – 标记为recursion。
  3. 运行你的项目(Cmd + R)

然后回到javascript的土地:

您需要导入react-native-keyboardevents。

 var KeyboardEvents = require('react-native-keyboardevents'); var KeyboardEventEmitter = KeyboardEvents.Emitter; 

然后在你的视图中,为键盘空间添加一些状态并从监听键盘事件中更新。

  getInitialState: function() { KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, (frames) => { this.setState({keyboardSpace: frames.end.height}); }); KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, (frames) => { this.setState({keyboardSpace: 0}); }); return { keyboardSpace: 0, }; }, 

最后,在你的渲染函数的底部添加一个间隔符,这样当它增加大小的时候,它会让你的东西变得很糟糕。

 <View style={{height: this.state.keyboardSpace}}></View> 

也可以使用animationAPI,但为了简单起见,我们只是在animation之后进行调整。

react-native-keyboard-aware-scroll-view解决了我的问题。 GitHub上的react-native-keyboard-aware-scroll-view

尝试这个:

 import React, { DeviceEventEmitter, Dimensions } from 'react-native'; 

 getInitialState: function() { return { visibleHeight: Dimensions.get('window').height } }, 

 componentDidMount: function() { let self = this; DeviceEventEmitter.addListener('keyboardWillShow', function(e: Event) { self.keyboardWillShow(e); }); DeviceEventEmitter.addListener('keyboardWillHide', function(e: Event) { self.keyboardWillHide(e); }); } 

 keyboardWillShow (e) { let newSize = Dimensions.get('window').height - e.endCoordinates.height; this.setState({visibleHeight: newSize}); }, keyboardWillHide (e) { this.setState({visibleHeight: Dimensions.get('window').height}); }, 

 render: function() { return (<View style={{height: this.state.visibleHeight}}>your view code here...</View>); } 

它为我工作。 当键盘被显示时,视图基本上会缩小,当它被隐藏时,视图会再次增长。

只是想提到,现在在RN中有一个KeyboardAvoidingView 。 只需导入它,并将其用作RN中的任何其他模块。

以下是提交到RN的链接:

https://github.com/facebook/react-native/commit/8b78846a9501ef9c5ce9d1e1​​8ee104bfae76af2e

它是从0.29.0

他们还在UIExplorer上包含了一个例子。

我使用TextInput.onFocus和ScrollView.scrollTo。

 ... <ScrollView ref="scrollView"> ... <TextInput onFocus={this.scrolldown}> ... scrolldown: function(){ this.refs.scrollView.scrollTo(width*2/3); }, 

@Stephen

如果你不介意没有在键盘出现的相同的速度高度animation,你可以使用LayoutAnimation,所以至less高度不会跳到位。 例如

从react-native导入LayoutAnimation并将以下方法添加到您的组件。

 getInitialState: function() { return {keyboardSpace: 0}; }, updateKeyboardSpace: function(frames) { LayoutAnimation.configureNext(animations.layout.spring); this.setState({keyboardSpace: frames.end.height}); }, resetKeyboardSpace: function() { LayoutAnimation.configureNext(animations.layout.spring); this.setState({keyboardSpace: 0}); }, componentDidMount: function() { KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace); KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace); }, componentWillUnmount: function() { KeyboardEventEmitter.off(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace); KeyboardEventEmitter.off(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace); }, 

一些animation例子是(我使用上面的弹簧):

 var animations = { layout: { spring: { duration: 400, create: { duration: 300, type: LayoutAnimation.Types.easeInEaseOut, property: LayoutAnimation.Properties.opacity, }, update: { type: LayoutAnimation.Types.spring, springDamping: 400, }, }, easeInEaseOut: { duration: 400, create: { type: LayoutAnimation.Types.easeInEaseOut, property: LayoutAnimation.Properties.scaleXY, }, update: { type: LayoutAnimation.Types.easeInEaseOut, }, }, }, }; 

更新:

请参阅下面的@ sherlock的答案,作为反应原生0.11键盘resize可以使用内置的function来解决。

你可以把几个方法合并成一个简单的东西。

在你的input上附加一个onFocus监听器

 <TextInput ref="password" secureTextEntry={true} onFocus={this.scrolldown.bind(this,'password')}/> 

我们的向下滚动方法看起来像这样:

 scrolldown(ref) { const self = this; this.refs[ref].measure((ox, oy, width, height, px, py) => { self.refs.scrollView.scrollTo({y: oy - 200}); }); } 

这告诉我们的滚动视图(记得添加一个参考)滚动到我们的焦点input的位置 – 200(这大致是键盘的大小)

 componentWillMount() { this.keyboardDidHideListener = Keyboard.addListener('keyboardWillHide', this.keyboardDidHide.bind(this)) } componentWillUnmount() { this.keyboardDidHideListener.remove() } keyboardDidHide(e) { this.refs.scrollView.scrollTo({y: 0}); } 

在这里,我们将滚动视图重置回顶部,

在这里输入图像描述

也许是晚了,但最好的解决scheme是使用本机库, IQKeyboardManager

只需将演示项目中的IQKeyboardManager目录拖放到您的iOS项目即可。 而已。 您也可以设置一些值,如启用isToolbar,或在AppDelegate.m文件中的文本input和键盘之间的空间。 有关定制的更多细节在我添加的GitHub页面链接中。

我正在使用一个更简单的方法,但它还没有animation。 我有一个组件状态称为“bumpedUp”,我默认为0,但设置为1时textInput获取焦点,如下所示:

在我的textInput:

 onFocus={() => this.setState({bumpedUp: 1})} onEndEditing={() => this.setState({bumpedUp: 0})} 

我使用brysgo答案来提高我的滚动视图的底部。 然后我使用onScroll来更新scrollview的当前位置。 然后,我发现这个React Native:获取元素的位置来获取textinput的位置。 然后我做一些简单的math来确定input是否在当前视图中。 然后我使用scrollTo移动最小的金额加上一个边距。 这很顺利。 下面是滚动部分的代码:

  focusOn: function(target) { return () => { var handle = React.findNodeHandle(this.refs[target]); UIManager.measureLayoutRelativeToParent( handle, (e) => {console.error(e)}, (x,y,w,h) => { var offs = this.scrollPosition + 250; var subHeaderHeight = (Sizes.width > 320) ? Sizes.height * 0.067 : Sizes.height * 0.077; var headerHeight = Sizes.height / 9; var largeSpace = (Sizes.height - (subHeaderHeight + headerHeight)); var shortSpace = largeSpace - this.keyboardOffset; if(y+h >= this.scrollPosition + shortSpace) { this.refs.sv.scrollTo(y+h - shortSpace + 20); } if(y < this.scrollPosition) this.refs.sv.scrollTo(this.scrollPosition - (this.scrollPosition-y) - 20 ); } ); }; }, 

我也遇到这个问题。 最后,我通过定义每个场景的高度来解决它,比如:

<Navigator ... sceneStyle={{height: **}} />

而且,我还使用第三方模块https://github.com/jaysoo/react-native-extra-dimensions-android获得真正的高度。;