React Native 踩坑日记(九) —— 键盘遮挡问题

综述

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

我的实际用例


示意图

第一部分,

介绍几个键盘遮挡相关的库.(但是这几个库我到最后都没有使用.)

RN 原生有提供几个和⌨️键盘相关的库.

Keyboard module

Keyboard -- react-native库中存在的,相当于 ios 原生中的键盘广播.(UIKeyboardWillShowNotification 那些)
摘录一段代码

class Demo extends Component {
    constructor(props) {
        super(props);
        this.keyboardDidShowListener = null;
        this.keyboardDidHideListener = null;
    }
    componentWillMount () {
        this.keyboardWillShowSub = Keyboard.addListener('keyboardWillShow', this.keyboardWillShow);
        this.keyboardWillHideSub = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide);
    }
    componentWillUnmount() {
        //卸载键盘弹出事件监听
        if (this.keyboardDidShowListener != null) {
            this.keyboardDidShowListener.remove();
        }
        //卸载键盘隐藏事件监听
        if (this.keyboardDidHideListener != null) {
            this.keyboardDidHideListener.remove();
        }
    }
    _keyboardDidShow (event) {
        let keyboardhHeight = event.endCoordinates.height;// 获取当前键盘高度
        // this._tableview.scrollToIndex({animated:true,index:keyboardhHeight,viewPosition:0});
        // this._tableview.scrollToOffset({animated:true,offset:keyboardhHeight});
    }
    _keyboardDidHide () {
        // alert('Keyboard Hidden');
    }

dismissKeyboard

调用路径
import dismissKeyboard from '../../../node_modules/react-native/Libraries/Utilities/dismissKeyboard';
这个库是专门用来手动控制键盘消失的. 调用很简单,就是用dismissKeyboard()
一般结合TextInput组件时,可以选择在onDrag(用户手动拖曳)之类的时机让键盘自行消失.

KeyboardAvoidingView

这个库我不大清楚,但是找了一些文章,包括我们下面要提到的Keyboard Aware ScrollView库相关的 Stack Overflow也看到有人提到这个系统提供的库.估计还是因为他的强耦合性.

react-native-keyboard-spacer

可以参考下下面这段的调用方法.
他的用法,是将这个组件和 textinput组件放在同级下使用.
缺点是无法控制多个输入框情况下的输入.而且必须和textinput 放同级,如果textinput外部又包了一层,就很难使用.

我尝试过写几个TextInput然后在最底下放一个,结果是,假设有 A,B,C 三个输入框, 我在 C 中输入,然后切换到 A 输入,这时候我其实只要 A 输入框上移,但结果是 C 也跟着上移了,而且底部会报一个错误,类似于告诉你 your previous settings has been overrided 也就是说之前写过的设置又被后一个覆盖,比较不靠谱.


     {
            this.textInput = textInput;
        }}
        style={[styles.textInput, {height: this.state.textInputHeight}]}

{Platform.OS === 'ios' && }

第二部分

先总结下碰到的若干问题.

我尝试过自己用Keyboard来获取键盘弹出/消失 事件的时机来解决键盘的遮挡问题.比如说

  • 在这个 Flatlistscrollview底部再加一个类似于键盘容器一样的东西(高度和键盘大小一样),让父组件在键盘弹出的时候,把它的高度变大,从而顶上来.
  • 或者直接对flatlist使用绝对布局({position:'absolute'}), 控制bottom,也就是底部约束.
    我参考过ios 原生控制键盘的方法,也就是用masonry来控制make.bottom.equalTo来改变底部约束.
  • 使用flatlistscrollToIndex,先让当前 cell滚动到页面最顶部,避免被遮挡.(具体可以参看React Native 踩坑日记(十) —— 使用 flatlist 的滚动处理键盘遮挡的问题.

上述的做法最后都失效了,原因有几个:

  • 键盘弹出的时机.
    这其实就是键盘事件的生命周期,和输入框的焦点聚焦(onFocus)的生命周期之间的问题.
    因为 RN 其实最后调用的是原生的键盘事件,所以键盘弹出的时机一定是优于 RN 的 UI 更新的时机的.
    我们可以分别在keyboardWillShow和输入框的onFocus两个回调函数中分别打断点,可以发现 keyboardWillShow或者说键盘相关的回调函数一定是先被调用到.
示意图

正因为 键盘先弹出,后进入onfocus, 我们如果要实现键盘弹出之前,我先去实现页面的滚动,似乎是不可能的(至少我目前还没发现可以)

  • 如果使用上面说的实现高度或约束的变化,都要去setState,也就意味着要刷新 UI, 此时,如果你的输入框的锁在的高度,小于键盘的高度,就会出现: 键盘弹出后立即缩回的情况.也就是说,按照了下面的逻辑在走

    • 键盘先探出
    • 调用了 setState
    • 页面被刷新
    • 刷新页面的同时,动到了底部键盘所处的 UI 高度区域
    • 键盘自动回缩

    这也就是为什么我们的逻辑无法实现的原因.

  • 另外, scrollToIndex 在 安卓上的支持不好,底下是会空出空白的部分的.

最终解决方案.

找了一大圈,最后还是上 GitHub 上搜了一个1.2K star的第三方库:Keyboard Aware ScrollView
详细的使用说明我就不啰嗦了(可以参看文章开头的链接),只是有几点特别要注意的,在这里记录下.

版本的问题
先来看支持的版本说明:

v0.4.0 requires RN>=0.48
v0.2.0 requires RN>=0.32.0.
v0.1.2 requires RN>=0.27.2 but you should use 0.2.0 in order to make it work with multiple scroll views.
v0.0.7 requires react-native>=0.25.0.
Use v0.0.6 for older RN versions.
  • 因为这个库其实蛮早就已经出来了,所以早期的版本还是只支持ListView的.我们 目前用的比较多的FlatList的支持是针对RN 0.48以后才有 flatlist的.目前我用不上.
  • 针对多输入框,其实只要用 scrollview就已经可以了.这个库提供的KeyboardAwareScrollView看上去也是它的子类(其实 js 中没有类的概念,姑且这么认为.)
    非要使用flatlist 的话,可以用另外一种方式来实现,(文章最后会贴下代码的片段)
  • 这条是友情提示. 如果你不小心用错了版本(比如你的 RN 是0.44,你用了0.4.0的 keyboard 库),系统报错不说,还有可能会把你先行的 node库给更改掉.我就踩过这个坑.经过 N 次的卸载重装 node, npm,都无法解决.最后用了下面这个土办法:

    所有的方式如果都失败了,请找一台运行正常的 Mac, 复制这个路径下的 node_modules 到本机的对应目录.
    /usr/local/lib/node_modules/npm


代码片段

  • 是自己定义的单个 Cell 的组件
  • this.dataSource是一个JSON Object Array,存放cell DataSource
    /* flat list 相关 */
    generateSingleCell = (item:Object, onSelectFunc:Function) => {
        return (
            
        );
    };

    generateTableViewComponents = ()=>{
        let cmpArray = [];
        for (let item of this.dataSource) {
            let onSelectFunc;
            switch (item.cellType) { // 根据类型,取相应的回调函数
                case cellType.inputCell:
                    onSelectFunc = this._onEndEditText;
                    break;
                case cellType.jmpNativeCell:
                    onSelectFunc = this._onPressJumpNativeCell;
                    break;
                case cellType.jmpCell:
                    onSelectFunc = this._onPressJumpCell;
                    break;
                case cellType.datePickerCell:
                    onSelectFunc = this._onPressDatePicher;
                    break;
                default:
                    break;
            }
            let cell = this.generateSingleCell(item,onSelectFunc);
            cmpArray.push(cell);
        }
        console.log('组合成的 cell =' + cmpArray);
        return cmpArray;
    };

    render() {
        let curSelDateCellDate = this.state[this._curSelDateCell]; //取出选中的那个 cell 的 date
        let listComponents = this.generateTableViewComponents();
        return (
            
                
                    {listComponents}
                
            
        );
    }

你可能感兴趣的:(React Native 踩坑日记(九) —— 键盘遮挡问题)