我已经见过针对本机应用程序自动滚动窗口的一种技巧,但我想知道在React Native中实现此目的的最佳方法...当某个<TextInput>
字段获得焦点并且在视图中位于较低位置时,键盘将覆盖文本字段。
您可以在示例UIExplorer的TextInputExample.js
视图中看到此问题。
有没有人有一个好的解决方案?
Answers:
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设计的需求。
import {findNodeHandle} from 'react-native'
stackoverflow.com/questions/37626851/…–
DeviceEventEmitter.addListener('keyboardDidShow', this.keyboardDidShow.bind(this));
我们结合了一些代码形式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,但为简单起见,我们仅在动画之后进行调整。
this.listView.getScrollResponder().scrollTo(rowID * rowHeight);
当行的TextInput接收到onFocus事件时,将对其进行调用。
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});
});
}
这告诉我们滚动视图(记住要添加一个引用)以向下滚动到焦点输入的位置-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,如下所示:
在我的textInput上:
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:获取元素的位置以获取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获取真实高度。