首页 > 编程 > JavaScript > 正文

详解React-Native解决键盘遮挡问题(Keyboard遮挡问题)

2019-11-19 16:05:11
字体:
来源:转载
供稿:网友

本文介绍了React-Native键盘遮挡问题,分享给大家

在开发中经常遇到需要输入的地方,RN给我们提过的TextInput虽然好用,可惜并没有处理遮挡问题。

很多时候键盘弹出来都会遮挡住编辑框,让人很头疼。

本来想在js.coach 库里面找一找第三方的插件,看到最好的一个就是React-native-keyboard-spacer了,然而我们还差一个东西,那就是获取键盘的高度。

这个我也查了半天并没有提供,获取没找到吧。于是只好自己写原生模块去获取键盘的高度了。

关于原生iOS获取键盘高度我就不多说了,网上一大堆,我直接贴上我的代码,自己根据RN写的原生模块:

// // KeyboardHeight.h // Jicheng6 // // Created by guojicheng on 16/11/7. // Copyright © 2016年 Facebook. All rights reserved. //  #import <UIKit/UIKit.h> #import "RCTEventEmitter.h" #import "RCTBridgeModule.h"  @interface KeyboardHeight : RCTEventEmitter<RCTBridgeModule>  -(void)heightChanged:(int)height;  @property (nonatomic, assign)int kbHeight;  @end 
// // KeyboardHeight.m // Jicheng6 // // Created by guojicheng on 16/11/7. // Copyright © 2016年 Facebook. All rights reserved. //  #import "KeyboardHeight.h"  @implementation KeyboardHeight  RCT_EXPORT_MODULE();  - (instancetype)init {  self = [super init];  if (self) {   self.kbHeight = 0;   [[NSNotificationCenter defaultCenter] addObserver:self                        selector:@selector(keyboardDidShow:)                          name:UIKeyboardDidShowNotification                         object:nil];  }  return self; }  -(void)keyboardDidShow:(NSNotification*) aNotification {  //获取键盘的高度  NSDictionary *userInfo = [aNotification userInfo];  NSValue *aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];  CGRect keyboardRect = [aValue CGRectValue];  if (_kbHeight != keyboardRect.size.height){   _kbHeight = keyboardRect.size.height;   [self heightChanged:_kbHeight];  } }  RCT_REMAP_METHOD(getKBHeight,          resolver:(RCTPromiseResolveBlock)resolve          rejecter:(RCTPromiseRejectBlock)reject) {  resolve([[NSNumber alloc]initWithInt:_kbHeight]); }  - (NSArray<NSString *> *)supportedEvents {  return @[@"heightChanged"]; }  -(void)heightChanged:(int)height {  [self sendEventWithName:@"heightChanged" body:[NSNumber numberWithUnsignedInt:height]]; }  @end 

这里其实我前面的博客也说过,一开始我想的是通过RCT_REMAP_METHOD去获得高度,可惜在键盘第一次弹出的时候,并不是弹出之后的高度,获取之后依然是0,所以添加了一个监听函数heightChanged,当记录的值和改变的值不一致时,调用监听函数,将值传给JS端。这样就可以在检测变化之后JS端做相应的变化。

好了,原生模块封装好了,接下来看js方面,这个也是老话题了,前面的博客都说了,直接贴代码:

import React, { Component } from 'react'; import {   AppRegistry,   StyleSheet,   Text,   View,   TouchableOpacity,   Alert,   TextInput,   PixelRatio,   Linking,   Keyboard,   NativeEventEmitter, } from 'react-native';  var Dimensions = require('Dimensions'); var ScreenWidth = Dimensions.get('window').width; var ScreenHeight = Dimensions.get('window').height;  var kbHeight = require('NativeModules').KeyboardHeight; const kbHeightEvt = new NativeEventEmitter(kbHeight); 
componentWillMount() {     this.heightChanged = kbHeightEvt.addListener('heightChanged', this._heightChanged.bind(this));   }   componentDidMount() {    }   componentWillUnmount() {     this.heightChanged.remove();   }   _heightChanged(data){     // console.log(data);     this.keyboardHeight = data;     this.changeMarginTop();//这里我是处理高度的   } 

这里已经拿到高度,接下来就好办了,就是加减问题。

我们需要拿到输入框在屏幕中的位置,然后和键盘的高度做比较,输入框的位置我们通过onLayout获取:

onLayoutParent(event){     if (this.orgLayoutParent == null){//获取的父组件的位置,因为要用到计算       this.orgLayoutParent = event.nativeEvent.layout;     }     console.log('parent layout: ', event.nativeEvent.layout);   }   onLayoutMail(event){//获取输入框的位置,这个位置是相对父组件的位置,所以上面需要获得父组件的     this.layoutMail = event.nativeEvent.layout;   }   onFocusMail(event){     this.focusName = 'mail';//定义一个标识,可以区分不同输入框     this.changeMarginTop();//统一处理高度的函数   }   onSubmitMail(){     drawLayout.setKBMoveY(0);//当输入完毕时,重置回原来的状态   }   changeMarginTop(){//计算移动的距离     var layout = null;     if (this.focusName == 'mail'){       layout = this.layoutMail;     }     if (layout && this.orgLayoutParent.y + layout.y + layout.height > ScreenHeight - this.keyboardHeight){       drawLayout.setKBMoveY(-(this.orgLayoutParent.y + layout.y + layout.height - ScreenHeight + this.keyboardHeight));     }else{//不对的置零处理       drawLayout.setKBMoveY(0);     }   }   render() {     return (       <View style={[styles.container, this.props.style ? this.props.style : {}]} onLayout={this.onLayoutParent.bind(this)}>         <View style={[styles.viewStyle, {marginTop: 10}]} onLayout={this.onLayoutMail.bind(this)}>//这里获取的是相对位置哦           <TextInput style={styles.textInputStyle}             onChangeText={this.onTextChange.bind(this)}             value={this.state.emailPath}             placeholder={'请输入邮箱'}             onFocus={this.onFocusMail.bind(this)}//当获取到焦点时触发             onSubmitEditing={this.onSubmitMail.bind(this)}/>//点击回车时的调用,这里可以根据需求去做           <TouchableOpacity onPress={this.onSubmitSend.bind(this)}>             <View style={[styles.sendButtonView, {}]}>               <Text style={styles.sendButtonText}>                 发送               </Text>             </View>           </TouchableOpacity>         </View>       </View>     );   } 

如果你是当前一个组件一个页面,就没必要像我这样做了,加了一个global,去记录它们的祖父组件(主要是整个页面向上移动)

距离我们也都算好了,接下来就是给drawLayout加一个动画,然后动起来不要那么突兀。

import React, { Component } from 'react'; import {  StyleSheet,  Text,  View,  TouchableOpacity,  Animated, } from 'react-native';  import SendEmail from './SendEmail';  export default class DrawLayout extends Component {  constructor(props){   super(props);   this.state={    kbShowY: new Animated.Value(0),//设置动画的初始值   };   global.drawLayout = this;//这里将自己保存到global里面,方便它的子组件调用  }  setKBMoveY(y){   Animated.timing(//这里用的是timing均匀变化,具体的参数,可以参考RN的文档,写的很详细了,这里就不嗦了。    this.state.kbShowY,{     toValue: y,//变化到目的位置     delay: 250,//延时250毫秒    },   ).start();//开始  }  componentWillUnmount() {   global.drawLayout = null;//降这个值赋值为空  }    render() {   return (    <Animated.View style={[styles.container, {marginTop: this.state.kbShowY}]} >//使用Animated.View     <SendEmail style={{marginTop: 10}}/>    </Animated.View>   );  } } 

这就大功告成了。接着截图看看效果,虽然有动画,没法弄动态图

  

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表