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

参考:http://blog.csdn.net/pz789as/article/details/53079025


在开发中经常遇到需要输入的地方,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>
    );
  }
}

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

最新的发现,可以看我这里: 点击打开链接


  


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苏小败在路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值