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

本文介绍了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>
  );
 }
}

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

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

(0)

相关推荐

  • ReactNative之键盘Keyboard的弹出与消失示例

    在开发中经常遇到需要输入的地方,所以就学习了一下ReactNative键盘Keyboard的弹出与消失的方法,留个笔记. 今天我们来说下RN对键盘事件的支持. 在React-native 的Component组件中有个Keyboard. github地址如下:https://github.com/facebook/react-native/tree/770091f3c13f7c1bd77c50f979d89a774001fbf0/Libraries/Components/Keyboard 我们先

  • 详解React Native与IOS端之间的交互

    前置准备 首先最好了解一点关于 oc 的语法知识 1.创建声明文件nativeModule.h #import <Foundation/Foundation.h> #import <React/RCTBridgeModule.h> @interface nativeModule : NSObject <RCTBridgeModule> @end 2.创建文件nativeModule.m #import <Foundation/Foundation.h> #i

  • 详解React Native中如何使用自定义的引用路径

    目录 RN的相对路径地狱 RN的自定义路径需要的依赖 解决自定义引用路径导致的eslint报错问题 RN的相对路径地狱 我刚接触RN时,就发现所有的demo中给出来的路径都是相对路径,我自己的练习项目中想改成自定义的绝对路径,但是发现并没有我做前端时熟悉的webpack.config.js,所以也就不知道该怎么改了.伴随着RN的学习和开发练习,我的代码也变得越来越多,越来越复杂,我逐渐发现RN的相对路径越来越麻烦,比如我把某个文件移动到另一个不同深度的文件夹中,那么就需要把所有引用这个文件的地方

  • 详解React Native监听Android回退按键与程序化退出应用

    详解React Native监听Android回退按键与程序化退出应用 前言 我们知道Android回退按键,会控制页面返回, 并且退出应用并非真正意义退出,仍在后台运行,所以在某些场景下需要监控android回退按键,那么在React Native中应该如何应用呢?我们具体来看看. BackAndroid 此模块用于监听硬件的back键操作. 看下具体代码: BackAndroid.addEventListener('hardwareBackPress', function() { if (!

  • 详解React Native项目中启用Hermes引擎

    目录 引言 一.启用 Hermes 引擎 1.1 Android 1.2 iOS 二.Hermes 引擎使用 2.1 检查 Hermes 引擎是否启用 2.2 绑定Hermes 2.3 使用DevTools在Hermes上调试JS 引言 目前,最新版本的React Native(0.70.0及以上版本)已经默认开启了Hermes引擎.而Hermes则是专门针对React Native应用而优化的全新JavaScript引擎,启用Hermes引擎可以优化启动时间,减少内存占用以及空间占用. 一.启

  • 详解React Native开源时间日期选择器组件(react-native-datetime)

    项目介绍 该组件进行封装一个时间日期选择器,同时适配Android.iOS双平台,该组件基于@remobile/react-native-datetime-picker进行开发而来 配置安装 npm install react-native-datetime --save 1.1.iOS环境配置 上面步骤完成之后,直接前台写js代码即可 1.2.Android环境配置 在android/setting.gradle文件中如下配置 ... include ':react-native-dateti

  • 详解React native fetch遇到的坑

    最近在自学react native,学习过程中遇到了不少的坑,下面将针对登录这一功能来详细介绍一下以下遇到的两个问题. 1.在请求数据的时候,一般情况下我们会直接提交Content-type是json数据格式的请求.类似 fetch('https://mywebsite.com/endpoint/', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', },

  • 详解React Native 屏幕适配(炒鸡简单的方法)

    前言 React Native 可以开发 ios 和 android 的 app,在开发过程中,势必会遇上屏幕适配(ios 好几种尺寸的屏幕以及 android 各种尺寸的屏幕)的问题,下面介绍一种几行代码搞定 RN 适配的方法! 屏幕适配的前置知识 RN 中的尺寸单位为 dp,而设计稿中的单位为 px 原理 虽然单位不同,但是元素所占屏幕宽度的比例是相同的 利用元素所占屏幕比例不变的特性,来将 px 转为 dp(这样实现屏幕适配的话,在不同尺寸的屏幕下,元素会等比放大或缩小) 公式如下: 设计

  • 详解React Native顶|底部导航使用小技巧

    导航一直是App开发中比较重要的一个组件,ReactNative提供了两种导航组件供我们使用,分别是:NavigatorIOS和Navigator,但是前者只能用于iOS平台,后者在ReactNative0.44版本以后已经被移除了. 好在有人提供了更好的导航组件,就是我们今天要讲的react-navigation,并且ReactNative官方更推荐我们使用此组件. 本篇文章只讲解基础用法,如果你想了解更多,请戳这里->戳我.  简介 react-navigation主要包括导航,底部tab,

  • 详解React Native 采用Fetch方式发送跨域POST请求

    Fetch以后是趋势,势必要取代传统的Ajax,而且RN框架支持Fetch.下面仅做了一个跨域请求的例子,在本域请求是一样的,而且更简单一些.客户端环境用的是RN写的一个页面,也可以用浏览器的console控制台模拟.后端服务用的是NodeJs express框架. 1)Fetch请求 //发送Ajax请求 sendAjax(){ //POST方式,IP为本机IP fetch("http://192.168.111.102:8085", { method: "POST&quo

  • 详解React Native网络请求fetch简单封装

    在原生应用开发中,为了方便业务开发人员使用,我们一般会对网络库进行一些上传封装,而不是直接使用,例如基于AFNetworking库的iOS请求上层封装,Android的诸如volley,retrofit等.在前端开发中,一般使用fetch进行网络请求,相关介绍请查看fetch示例.其实对于开发来说,系统提供的fetch已经够用了,但是为了代码的整体结构,建议对fetch进行简单的Get/Post封装. 若不封装,我们看一下传统的写法: fetch('http://www.pintasty.cn/

随机推荐