91

我已经看到了本机应用程序自动滚动窗口的这种 hack,但想知道在 React Native 中执行此操作的最佳方法...当一个<TextInput>字段获得焦点并位于视图中的较低位置时,键盘将覆盖文本字段。

您可以在示例 UIExplorer 的TextInputExample.js视图中看到此问题。

有没有人有好的解决方案?

4

14 回答 14

85

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 设计的需求。

于 2015-09-15T19:11:51.650 回答
27

Facebook开源 KeyboardAvoidingView了 react native 0.29 来解决这个问题。文档和使用示例可以在这里找到。

于 2016-07-09T22:17:20.777 回答
12

我们将 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;
于 2015-11-07T17:17:30.437 回答
10

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

  1. 在 XCode 中,在项目导航器中,右键单击 Libraries ➜ Add Files to [your project's name] Go to node_modules ➜ react-native-keyboardevents 并添加 .xcodeproj 文件
  2. 在 XCode 的项目导航器中,选择您的项目。将keyboardevents 项目中的lib*.a 添加到项目的Build Phases ➜ Link Binary With Libraries 单击您之前在项目导航器中添加的.xcodeproj 文件,然后转到Build Settings 选项卡。确保打开“全部”(而不是“基本”)。查找 Header Search Paths 并确保它同时包含 $(SRCROOT)/../react-native/React 和 $(SRCROOT)/../../React - 将两者都标记为递归。
  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>

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

于 2015-04-16T15:17:05.847 回答
7

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

于 2016-05-21T15:25:51.403 回答
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>);
}

...

它对我有用。显示键盘时视图基本上会缩小,隐藏时会再次增长。

于 2016-03-05T09:22:47.797 回答
4

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

这是 RN 上提交的链接:

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

它从 0.29.0 开始可用

他们还包括一个关于 UIExplorer 的示例。

于 2016-07-25T06:21:11.840 回答
4

也许为时已晚,但最好的解决方案是使用本机库IQKeyboardManager

只需将 IQKeyboardManager 目录从演示项目拖放到您的 iOS 项目。而已。您还可以设置一些值,例如启用 isToolbar,或者在 AppDelegate.m 文件中设置文本输入和键盘之间的空间。有关自定义的更多详细信息,请参见我添加的 GitHub 页面链接。

于 2017-01-29T20:40:00.987 回答
3

我使用了 TextInput.onFocus 和 ScrollView.scrollTo。

...
<ScrollView ref="scrollView">
...
<TextInput onFocus={this.scrolldown}>
...
scrolldown: function(){
  this.refs.scrollView.scrollTo(width*2/3);
},
于 2015-06-13T01:29:09.770 回答
2

@斯蒂芬

如果您不介意高度动画与键盘出现的速率完全相同,您可以使用 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 开始,可以使用内置功能解决键盘大小调整问题。

于 2015-06-11T06:35:51.327 回答
2

您可以将一些方法组合成更简单的方法。

在你的输入上附加一个 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});
}

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

在此处输入图像描述

于 2016-06-23T19:39:26.023 回答
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,边距是硬编码的,但它有效:)

于 2015-06-22T21:48:43.950 回答
0

我使用 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 );
                        }
                     );
                };
            },
于 2015-07-29T06:08:14.907 回答
0

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

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

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

于 2016-04-26T07:48:25.033 回答