React Native —— TextInput 组件与键盘遮

2018-06-21  本文已影响155人  黑羽肃霜

综述

键盘遮挡问题,应该是 RN 中常见的了,网上有很多参考文章.但是这次开发的页面中涉及到多行输入框的问题。
键盘响应的几种办法

我个人写的一个库:功能区域输入库 react-native-FuncInpu

鉴于之前开发过一个处理键盘高度与自适应的组件库,这里对若干个问题作一个记录。记录的内容包括以下几块:


安卓与 iOS实现键盘弹出的区别

iOS的情况

熟悉iOS开发的同学一定很清楚,在iOS中键盘的遮挡问题就是自己处理的。需要代码中去监听键盘事件的notification,然后自己处理对应的可能被遮挡控件的frame的变化。

UIKIT_EXTERN NSNotificationName const UIKeyboardWillShowNotification __TVOS_PROHIBITED;
UIKIT_EXTERN NSNotificationName const UIKeyboardDidShowNotification __TVOS_PROHIBITED;
UIKIT_EXTERN NSNotificationName const UIKeyboardWillHideNotification __TVOS_PROHIBITED;
UIKIT_EXTERN NSNotificationName const UIKeyboardDidHideNotification __TVOS_PROHIBITED;
UIKIT_EXTERN NSNotificationName const UIKeyboardWillChangeFrameNotification  NS_AVAILABLE_IOS(5_0) __TVOS_PROHIBITED;
UIKIT_EXTERN NSNotificationName const UIKeyboardDidChangeFrameNotification   NS_AVAILABLE_IOS(5_0) __TVOS_PROHIBITED;

另外对于iOS而言,第三方键盘存在着较为特别的情况。从iOS 8开始苹果支持第三方键盘输入,但是市场还会出现键盘无法弹出的问题。

本人使用的是搜狗的第三方键盘,从前经常出现无法弹出,必须要去 设置 -> 通用 -> 键盘 -> 搜狗键盘 -> 必须将允许完全访问关闭再开启,才能重新启用。但是这个问题应该现在是不再会存在了。

从实现的效果来说,第三方键盘比起原生键盘,会多出类似于收起按键等等的功能(原生键盘没有)。

32F3C45F7E8DEA06CE7FD57F905EE870.png

而且第三方键盘实质上是会先调用原生键盘,使得上文提到的notification生效,然后再让自己的键盘生效的。以我调试时候遇到的情况为例,回调会收到键盘的占位高度,如果使用第三方键盘,高度会收到两次。以上图中的搜狗键盘为例,会收到242282两个高度,最后固定的高度是282

安卓的情况

安卓的键盘弹出不存在 ios 的特殊情况。因为键盘的部分是独立于我们页面之外的。也就是说他会自己处理原先被遮挡部分的向上滚动。这里就不作赘述。


键盘遮挡解决的思路

通常的解决思路是,将所有的 TextInput组件的实例都包裹在一个scrollView中。当键盘起来的时候,整体的ScrollView做一个相应的滚动偏移。
文首中介绍的那个第三方库 react-native-keyboard-aware-scroll-view 就是借鉴了这一点来实现.

但是有一点是不可避免的,就是使用了这套方案,从视觉上,一定是键盘先弹出,然后所有的内容页面才会想上滚动。这和原生页面情况下键盘的弹出和页面的上移几乎是同时发生的效果无法比拟。

对比图.gif

这里闪动的有一点快,仔细看是可以看出二者的展示区别的。

产生这种情况的原因是因为rn必须要先从iOS原生获取到键盘的时间之后,再去进行相关的响应。这其中的生命周期及调用顺序,会在最后一个部分 生命周期 中讲到。


TextInput props的坑

针对键盘输入和遮挡的问题,主要是使用react native官方提供的TextInput组件。但是其中有若干属性会影响到键盘遮挡输入内容。下面一一来列举。


生命周期

这里要讨论的主要就是针对3个情况下的生命周期及回调函数

键盘弹出

正如上文提到的,当我开始点击一个输入框时,其实实现的是有3两件事:光标的聚焦、键盘的弹出、外部scrollView的滚动
但是这三件事各自的回调都是异步发生的,也就是说,他们的回调的时间顺序可能和我们设想和期望的不一样。

我们先来罗列这三个事情发生的时候,他们各自的回调。

输入框换行

onContentSizeChange 回调。这里再强调一次,当TextInput 被初始化渲染的时候,onContentSizeChange 也会执行,回调的高度根据设定的font大小来决定

键盘回缩

键盘回缩有3种情况会触发到:

而且会和上面类似产生两大类回调:

这里还有一个小坑,如果我们设置的发送按钮是一个 button,或者是一个TouchOpacity,那么点击发送以后,会先响应点击了空白区域这一设定,然后再执行 buttononPress 回调。我这里的处理方法是,不使用按键性质的组件,直接采用 <View>onTouchStart 回调,规避了这个问题的发生

send.png

另外,在 ios 下调用 Keyboard.dismiss() 且当前输入键盘为第三方键盘时,有时候会消失失败。我做的方法是键盘调用 Keyboard.dismiss() 之后隔50ms 再调用一次。

在频繁的切换弹出和回缩的过程中,如果没有采用标志位去对这些凌乱的回调进行管控,让他们跑在我们想要的顺序中,就会达不到我们预期想要实现的效果。
鉴于如何实现一个理想化的输入框切换不是本篇想要讨论的内容,这里只讨论如果你要按顺序执行你想要执行的时序,该如何避免其中可能导致的坑。

想了解的读者可以参看 我的一个 git 开源库 有文档说明和参考代码。这了不赘述

解决上面这几个问题的方法其实说简单也简单。
一个是要采用标志位来避免进入一些重复的回调,(例如 keyboardDismiss我在上头都会执行两次,第一次会进入到willChangeFrame,但是我进去之后设置了一个标志位,第二次再进去,因为这个标志位,我就不会再执行一次后续的代码了)
第二就是在需要做一些调用的时候加上几十毫秒的延时,让另一个回调先执行。

流程图

下面是两张图借用我之前开发过的一个带功能区域的输入组件(效果参考微信聊天页面底下的功能输入区域)中的流程图,可以感受下生命周期对开发过程中的影响。

功能输入——唤起键盘流程.png 功能输入 —— 键盘消失流程.png
上一篇 下一篇

猜你喜欢

热点阅读