使用 React Native 自定义安全键盘(iOS 和 Android 双适配)

自定义键盘的实现效果以下:

iOS端:
react

ios-keyboard.gif

Android端:
android-keyboard.gif

实现功能(iOS和Android):android

  • 输入框没有自定义,直接使用RN的TextInput
  • 点击完成能够移除焦点
  • 长按回退键能够删除光标前全部字符
  • 字母和字符键盘点击会显示Tip
  • 字母、字符、数字键盘可相互快速切换(为视图作了懒加载和缓存)

大体思路

一、如何隐藏系统键盘
用RN为iOS和Android双平台共用一套自定义键盘,使用RN的TextInput做为输入框,这样TextInput该怎么使用就怎么使用,所以问题的关键就在于如何隐藏系统键盘。在iOS中使用inputView能够轻松实现自定义键盘,在Android中自定义键盘并不是像iOS那么直接,须要手动隐藏系统键盘,而后监听输入框的焦点事件来控制自定义键盘的显示和隐藏。
能够看如下两个示例大体了解下:
iOS自定义键盘
Android 自定义键盘实现ios

Android中自定义键盘有一个让人容易误解的地方,就是KeyboardView。能够在Android 自定义键盘实现Demo中看到有hideSystemSoftKeyboard方法,用来把EditText的系统键盘给隐藏,而后再使用KeyboardView加入到视图的底部布局中,KeyboardView使用KeyboardView.OnKeyboardActionListener来监听键盘点击,使用Keyboard来给定键盘布局(使用XML描述布局),而后能够动态修改KeyboardKeyboardView.OnKeyboardActionListener来实现不一样的自定义键盘。因而可知,使用RN作自定义键盘在hideSystemSoftKeyboard后就能够打止了,不须要使用KeyboardView,而是使用ReactRootView,而后在RN中绘制键盘。git

二、自定义键盘与原生输入组件如何交互github

  • RN -> Native
    • RN的TextInput要告知Native它须要什么类型的自定义键盘,所以须要把TextInput的ReactTag和键盘类型传给Native。
    • RN的键盘每次点击一个按钮须要通知Native,而且传入RN的Keyboard对应的TextInput组件(在Android中是EditText,在iOS中是TextFiled和TextView)的ReactTag,Native能够经过ReactTag来定位到具体的Native输入框,而后再根据RN中传来的命令来修改输入框的内容从而实现同步。
  • Native -> RN
    • iOS
      • Native须要建立一个RCTRootView,而后将一些初始化参数传入(如自定义键盘类型,TextView对应的ReactTag)
      • 将RCTRootView赋值给ReactTag对应的TextView的inputView。
    • Android
      • Native拿到ReactTag对应的EditText,禁止它的系统键盘弹出。
      • Native须要建立ReactRootView加入到Activity的底部布局中当作键盘使用。
      • 将ReactRootView加入到EditText的tag中,而后监听EditText的焦点改变来控制相对应的键盘的显示和隐藏。

时序图以下:react-native

Paste_Image.png

RN中文网的自定义键盘的GitHub地址:github.com/reactnative…
关于ReactTag的基本原理(iOS版):awhisper.github.io/2016/07/02/…缓存

绘制

将上面的流程和代码弄清后就只剩下RN的键盘界面问题了。性能优化

数字键盘的布局和绘制:
参考代码:github.com/beefe/react…app

字母键盘的布局示意图:ide

image.png

字符键盘的布局示意图:

image.png

字母和符号键盘的Tip绘制:
最初想法:每一个key都为一个单独的component,里面有个TouchableHighlight按钮和与按钮并级的Tip组件,在onPressIn和onPressOut之间控制Tip的显示与隐藏。

不可行:以上方案在Android端不可行,由于Android中RN不能使用overflow:visible属性,在0.41版本中可使用FlatUIImplement实现overflow:visible属性,可是在0.42及以上版本使用会报错(不过如今解决了,我没试)。

曲线救国想法:使用measureLayout获得key相对keyboard的位置,而后在keyboard层级绘制Tip,而不在key组件中绘制Tip。这个方案基本可行,可是在Android端仍是须要解决如下问题:
一、Tip可能会超过keyboard的边界,所以第一行仍是会被截掉,因此须要把键盘的高度要加高一些以容纳第一行的Tip。
二、keyboard的背景色会遮挡下层视图,因此要对keyboard的背景作透明。
三、keyboard加高的部分会遮挡下层视图对手势的响应,因此对加高的部分要放弃事件响应。

解答参考了stackoverflow上的提问:Element overflow hidden in React-Native Android

绘制难点:
iOS端Tip外形的绘制:使用react-native-svg绘制,个人绘制代码
Android阴影的实现:使用以react-native-svg为基库作成的react-native-shadow

注:我并无在Android中使用阴影样式,而是仿照f8app的写法在不一样平台使用不一样样式

性能优化

由于字母和字符键盘一次性绘制了30多个按钮在屏幕上,会致使在低端Android机能感受到卡顿(调成Debug模式会很明显,在Dev下打开'Show Perf Monitor'会发现首屏渲染时JS线程掉帧很明显),我在这作了个测试:一次性渲染40个按钮,能够运行看一下卡顿状况。
所以渲染出的数字、字母和字符键盘的DOM分别要被缓存起来,这样才不会在频繁切换键盘时感受到卡顿。
若是首屏出现的是数字键盘,那么字母键盘和字符键盘就不该该被放在渲染树中,作到用到特定键盘时再去加载这个键盘。

根据React 源码剖析系列 - 难以想象的 react diff,要作到高效的渲染尽可能不能改变Dom树的结构,React并无提供removeViewaddView之类的方法(固然你能够经过带 _ 的私有方法实现),Element(使用JSX或者React. createElement建立出来的)其实并不占多大空间,真正渲染的是DOM(能够经过ref获取),使用DisplayView能够控制DOM的隐藏和显示,让视图在第一次使用时才在DOM树建立,在隐藏和显示时不用改变DOM树的结构。

示例代码

customKeyboard

安利两个工具

react-native-storybook
UI组件的开发工具,能够浏览组件库,查看各个组件的各个状态,以及开发和测试组件。

react-devtools

image.png

欢迎关注个人简书主页:www.jianshu.com/u/b92ab7b3a… 文章同步更新^_^

相关文章
相关标签/搜索