当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 。
- 在XCode中,在项目导航器中,右键单击库➜添加文件到[您的项目名称]转到node_modules➜react-native-keyboardevents并添加.xcodeproj文件
- 在XCode中,在项目导航器中select您的项目。 将keyboardevents项目中的lib * .a添加到项目的构build阶段➜链接使用库的二进制文件在项目导航器中单击之前添加的.xcodeproj文件,然后转到“构build设置”选项卡。 确保“全部”开启(而不是“基本”)。 查找标题searchpath并确保它包含$(SRCROOT)/../ react-native / React和$(SRCROOT)/../../ React – 标记为recursion。
- 运行你的项目(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/8b78846a9501ef9c5ce9d1e18ee104bfae76af2e
它是从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获得真正的高度。;