我已经看到了本机应用程序自动滚动窗口的这种 hack,但想知道在 React Native 中执行此操作的最佳方法...当一个<TextInput>
字段获得焦点并位于视图中的较低位置时,键盘将覆盖文本字段。
您可以在示例 UIExplorer 的TextInputExample.js
视图中看到此问题。
有没有人有好的解决方案?
我已经看到了本机应用程序自动滚动窗口的这种 hack,但想知道在 React Native 中执行此操作的最佳方法...当一个<TextInput>
字段获得焦点并位于视图中的较低位置时,键盘将覆盖文本字段。
您可以在示例 UIExplorer 的TextInputExample.js
视图中看到此问题。
有没有人有好的解决方案?
2017年答案
这KeyboardAvoidingView
可能是现在最好的方法。在这里查看文档。Keyboard
与为开发人员提供更多控制来执行动画的模块相比,它非常简单。Spencer Carli 在他的媒体博客上展示了所有可能的方法。
2015年答案
执行此操作的正确方法react-native
不需要外部库,利用本机代码并包含动画。
首先定义一个函数来处理onFocus
每个TextInput
(或您想要滚动到的任何其他组件)的事件:
// 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
参数来满足您特定 UI 设计的需求。
我们将 react-native-keyboard-spacer 中的一些代码和来自 @Sherlock 的代码结合起来创建了一个 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。
然后回到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>
也可以使用动画api,但为了简单起见,我们只是在动画之后进行调整。
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>);
}
...
它对我有用。显示键盘时视图基本上会缩小,隐藏时会再次增长。
只是想提一下,现在KeyboardAvoidingView
RN中有一个。只需导入它并将其用作 RN 中的任何其他模块。
这是 RN 上提交的链接:
https://github.com/facebook/react-native/commit/8b78846a9501ef9c5ce9d1e18ee104bfae76af2e
它从 0.29.0 开始可用
他们还包括一个关于 UIExplorer 的示例。
也许为时已晚,但最好的解决方案是使用本机库IQKeyboardManager
只需将 IQKeyboardManager 目录从演示项目拖放到您的 iOS 项目。而已。您还可以设置一些值,例如启用 isToolbar,或者在 AppDelegate.m 文件中设置文本输入和键盘之间的空间。有关自定义的更多详细信息,请参见我添加的 GitHub 页面链接。
我使用了 TextInput.onFocus 和 ScrollView.scrollTo。
...
<ScrollView ref="scrollView">
...
<TextInput onFocus={this.scrolldown}>
...
scrolldown: function(){
this.refs.scrollView.scrollTo(width*2/3);
},
@斯蒂芬
如果您不介意高度动画与键盘出现的速率完全相同,您可以使用 LayoutAnimation,这样至少高度不会跳到位。例如
从 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);
},
一些示例动画是(我使用上面的弹簧):
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 答案,从 react-native 0.11 开始,可以使用内置功能解决键盘大小调整问题。
您可以将一些方法组合成更简单的方法。
在你的输入上附加一个 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});
});
}
这告诉我们的滚动视图(记得添加一个 ref)向下滚动到我们聚焦输入的位置 - 200(它大约是键盘的大小)
componentWillMount() {
this.keyboardDidHideListener = Keyboard.addListener(
'keyboardWillHide',
this.keyboardDidHide.bind(this)
)
}
componentWillUnmount() {
this.keyboardDidHideListener.remove()
}
keyboardDidHide(e) {
this.refs.scrollView.scrollTo({y: 0});
}
在这里,我们将滚动视图重置回顶部,
我正在使用一种更简单的方法,但它还没有动画。我有一个名为“bumpedUp”的组件状态,我默认为 0,但当 textInput 获得焦点时设置为 1,如下所示:
在我的文本输入上:
onFocus={() => this.setState({bumpedUp: 1})}
onEndEditing={() => this.setState({bumpedUp: 0})}
我还有一种样式,可以为屏幕上所有内容的包装容器提供下边距和负上边距,如下所示:
mythingscontainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
},
bumpedcontainer: {
marginBottom: 210,
marginTop: -210,
},
然后在包装容器上,我设置如下样式:
<View style={[styles.mythingscontainer, this.state.bumpedUp && styles.bumpedcontainer]}>
因此,当“bumpedUp”状态设置为 1 时,bumpedcontainer 样式就会启动并将内容向上移动。
Kinda hacky,边距是硬编码的,但它有效:)
我使用 brysgo 回答来提高我的滚动视图的底部。然后我使用 onScroll 更新滚动视图的当前位置。然后我找到了这个React Native:Getting the position of an element to get the position of the textinput。然后我做一些简单的数学运算来确定输入是否在当前视图中。然后我使用 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来获取真实高度。