当 TextInput 有焦点时,如何从键盘后面自动滑出窗口?
Posted
技术标签:
【中文标题】当 TextInput 有焦点时,如何从键盘后面自动滑出窗口?【英文标题】:How to auto-slide the window out from behind keyboard when TextInput has focus? 【发布时间】:2015-06-01 12:32:32 【问题描述】:我已经看到了本机应用程序自动滚动窗口的这种 hack,但想知道在 React Native 中执行此操作的最佳方法...当 <TextInput>
字段获得焦点并位于视图中的较低位置时,键盘将覆盖文本字段。
您可以在示例 UIExplorer 的 TextInputExample.js
视图中看到此问题。
谁有好的解决方案?
【问题讨论】:
我建议将此作为问题添加到 Github 跟踪器上,看看是否有任何结果,因为这将是一个非常常见的投诉。 【参考方案1】:2017 年答案
KeyboardAvoidingView
可能是现在最好的方式。查看文档here。与Keyboard
模块相比,它非常简单,它让开发人员可以更好地控制执行动画。 Spencer Carli 在his medium blog 上展示了所有可能的方法。
2015 年答案
在react-native
中执行此操作的正确方法不需要外部库,利用本机代码并包含动画。
首先定义一个函数来处理每个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
参数,以满足您特定 UI 设计的需求。
【讨论】:
这是一个不错的发现,但对我来说这还不够,因为虽然 ScrollView 将确保 TextInput 在屏幕上,但 ScrollView 仍在键盘下方显示用户无法显示的内容'滚动到。设置 ScrollView 属性“keyboardDismissMode=on-drag”允许用户关闭键盘,但如果键盘下方没有 足够 滚动内容,体验会有点不协调。如果 ScrollView 只需要首先因为键盘而滚动,并且您禁用弹跳,那么似乎没有办法关闭键盘并显示下面的内容 @miracle2k - 我有一个功能可以在输入模糊时重置滚动视图位置,即当键盘关闭时。也许这对你的情况有帮助? @Sherlock 模糊滚动视图重置功能是什么样的?顺便说一句很棒的解决方案:) 在较新的 React Native 版本中,您需要调用: * import ReactNative from 'react-native'; * 在调用之前 * ReactNative.findNodeHandle() * 否则应用会崩溃 现在import findNodeHandle from 'react-native'
***.com/questions/37626851/…【参考方案2】:
Facebook open sourced KeyboardAvoidingView
在 react native 0.29 中解决了这个问题。文档和使用示例可以在here找到。
【讨论】:
当心KeyboardAvoidingView,它就是不好用。它并不总是像您期望的那样运行。文档几乎不存在。 文档和行为现在越来越好了 我遇到的问题是,KeyboardAvoidingView 在我的 iPhone 6 模拟器上将键盘高度测量为 65,因此我的视图仍然隐藏在键盘后面。 我能管理它的唯一方法是通过DeviceEventEmitter.addListener('keyboardDidShow', this.keyboardDidShow.bind(this));
触发的底部填充方法
在 React Native v0.65 中,这里是 KeyboardAvoidingView【参考方案3】:
我们结合了 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;
【讨论】:
有什么简单/明显的方法可以阻止键盘在 ios 模拟器中显示? 你试过Command+K(硬件->键盘->切换软件键盘)吗? 在这里尝试修改版本:gist.github.com/dbasedow/f5713763802e27fbde3fc57a600adcd3 我相信这会更好,因为它不依赖任何我认为是脆弱的 imo 的超时。【参考方案4】:首先你需要安装react-native-keyboardevents。
-
在 XCode 中,在项目导航器中,右键单击库 ➜ 添加
文件到 [您的项目名称] 转到 node_modules ➜
react-native-keyboardevents 并添加 .xcodeproj 文件
在 XCode 中,在
项目导航器,选择您的项目。从键盘事件添加 lib*.a
项目到您项目的构建阶段➜链接二进制与库单击
您之前在项目导航器中添加的 .xcodeproj 文件并转到 Build
设置选项卡。确保打开“全部”(而不是“基本”)。
查找标题搜索路径并确保它包含两者
$(SRCROOT)/../react-native/React 和 $(SRCROOT)/../../React - 标记
都是递归的。
运行您的项目 (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>
也可以使用动画api,但为了简单起见,我们只是在动画之后进行调整。
【讨论】:
看到代码示例/有关如何制作动画的更多信息会很棒。跳转非常笨拙,并且仅使用“将显示”和“确实显示”方法,我无法完全弄清楚如何猜测键盘动画将多长时间或从“将显示”到多高。 react-native@0.11.0-rc 现在通过 DeviceEventEmitter 发送键盘事件(例如,“keyboardWillShow”),因此您可以为这些事件注册监听器。然而,在处理 ListView 时,我发现在 ListView 的滚动视图上调用 scrollTo() 效果更好:this.listView.getScrollResponder().scrollTo(rowID * rowHeight);
当它接收到 onFocus 事件时,它会在一行的 TextInput 上调用。
这个答案不再有效,因为 RCTDeviceEventEmitter 完成了这项工作。【参考方案5】:
react-native-keyboard-aware-scroll-view 为我解决了这个问题。 react-native-keyboard-aware-scroll-view on GitHub
【讨论】:
【参考方案6】:试试这个:
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 0.21.0) ***.com/a/35874233/3346628 使用 this.keyboardWillHide.bind(this) 而不是 self 使用键盘代替 DeviceEventEmitter【参考方案7】:只是想提一下,现在 RN 中有一个 KeyboardAvoidingView
。只需将其导入并用作 RN 中的任何其他模块即可。
这是 RN 上提交的链接:
https://github.com/facebook/react-native/commit/8b78846a9501ef9c5ce9d1e18ee104bfae76af2e
从 0.29.0 开始提供
他们还在 UIExplorer 上提供了一个示例。
【讨论】:
【参考方案8】:也许为时已晚,但最好的解决方案是使用原生库,IQKeyboardManager
只需将 IQKeyboardManager 目录从演示项目拖放到您的 iOS 项目。而已。您还可以设置一些值,例如启用 isToolbar,或者在 AppDelegate.m 文件中设置文本输入和键盘之间的空间。有关自定义的更多详细信息,请参见我添加的 GitHub 页面链接。
【讨论】:
这是一个很好的选择。另请参阅 github.com/douglasjunior/react-native-keyboard-manager 了解为 ReactNative 包装的版本 - 它易于安装。【参考方案9】:我使用了 TextInput.onFocus 和 ScrollView.scrollTo。
...
<ScrollView ref="scrollView">
...
<TextInput onFocus=this.scrolldown>
...
scrolldown: function()
this.refs.scrollView.scrollTo(width*2/3);
,
【讨论】:
【参考方案10】:@斯蒂芬
如果您不介意高度的动画效果与键盘出现的速度完全相同,您可以使用 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 开始,可以使用内置功能解决键盘大小调整问题。
【讨论】:
【参考方案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);
这里我们将滚动视图重置回顶部,
【讨论】:
@能否请您提供您的 render() 方法?【参考方案12】:我正在使用一种更简单的方法,但它还没有动画。我有一个名为“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 样式就会启动并将内容向上移动。
有点老套,边距是硬编码的,但它可以工作:)
【讨论】:
【参考方案13】:我使用 brysgo 答案来提升滚动视图的底部。然后我使用 onScroll 更新滚动视图的当前位置。然后我找到了这个React Native: Getting the position of an element 来获取文本输入的位置。然后我做一些简单的数学运算来确定输入是否在当前视图中。然后我使用 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 );
);
;
,
【讨论】:
【参考方案14】:我也遇到过这个问题。最后我通过定义每个场景的高度来解决,比如:
<Navigator
...
sceneStyle=height: **
/>
而且,我还使用第三方模块https://github.com/jaysoo/react-native-extra-dimensions-android 来获取真实高度。
【讨论】:
以上是关于当 TextInput 有焦点时,如何从键盘后面自动滑出窗口?的主要内容,如果未能解决你的问题,请参考以下文章
react native 隐藏键盘 TextInput失去焦点
在关注textinput时,保持textinput和键盘的小差距
在本机反应中单击 TextInput 字段外部时失去焦点并关闭键盘?
在输入之外单击时,RN TextInput 不会失去焦点并隐藏它的键盘