ReactNative踩坑之TextInput

身为一个web开发者,从来没想到自己会被一个小小的文本输入框折腾成这样。

最近在使用TextInput组件过程中,遇到的一些问题,在此做个分享和记录。

1.键盘遮挡

在web开发时,我们不需要考虑键盘的遮挡,因为系统会自动处理,但是在native这方面需要手动处理,还好官方已经出品了原生解决方案:KeyboardAvodingView

android可以通过设置android:windowSoftInputMode="adjustPan"系统自动处理键盘遮挡,这种情况下不需要使用该组件

先上代码

 <KeyboardAvoidingView
      style={{flex:1}}
      behavior="padding"
      keyboardVerticalOffset={64}
    >
      <ScrollView style={{flex:1}}>
        {/*.....*/}
      </ScrollView>
    </KeyboardAvoidingView>

看下文档便知,比较有用的就是这两个属性了

  • keyboardVerticalOffset
  • behavior

keyboardVerticalOffset

有些情况下你的视图可能距离屏幕顶部有一段距离,这个时候需要用这个进行修正。

网上教程很少详细说这个属性,但是这个属性非常重要。

先让我们看下KeyboardAvoidingView源码的实现

relativeKeyboardHeight(keyboardFrame: ScreenRect): number {
    const frame = this.frame;
    if (!frame || !keyboardFrame) {
      return 0;
    }

    const keyboardY = keyboardFrame.screenY - this.props.keyboardVerticalOffset;

    // Calculate the displacement needed for the view such that it
    // no longer overlaps with the keyboard
    return Math.max(frame.y + frame.height - keyboardY, 0);
},

onKeyboardChange(event: ?KeyboardChangeEvent) {
    if (!event) {
      this.setState({bottom: 0});
      return;
    }

    const {duration, easing, endCoordinates} = event;
    const height = this.relativeKeyboardHeight(endCoordinates);

    if (duration && easing) {
      LayoutAnimation.configureNext({
        duration: duration,
        update: {
          duration: duration,
          type: LayoutAnimation.Types[easing] || 'keyboard',
        },
      });
    }
    this.setState({bottom: height});
  },

可以看到该组件其实是在键盘打开关闭状态改变的时候,更新了botttom这个状态,而这个值是通过坐标计算而来的。

this.frame 是组件的根节点View onLayout回调得到值。

这里先用虚拟键盘在屏幕的y坐标减去了传入的keyboardVerticalOffset获取到键盘的y轴偏移量,然后frame.y + frame.height计算出容器距离父元素顶部的距离,减去键盘的y轴偏移量,就得出了容器需要偏移的距离。

为什么要减去一个偏移量呢?打个比方,我们的页面都会有状态栏,会有头部(例如使用react-navigation),这个时候我们在使用KeyboardAvoidingView的时候,可能并没有从屏幕顶部进行包裹,因为onLayout得到的y坐标是相对于父元素的,所以就会造成键盘的screenYframe.y没有以一个起点作为参考,所以需要手动修正这一块距离。

其实把计算改成:

    return Math.max(frame.y + this.props.keyboardVerticalOffset + frame.height  -  keyboardFrame.screenY, 0);

这样就比较好理解了。

behavior

顾名思义就是以何种方式处理,看源码得知主要做了如下处理

  • height
    键盘打开时把容器的高度减去了上面计算出来的bottom的值,关闭时又恢复原来大小
  • padding
    键盘打开时给容器设置了paddingBottom: bottom
  • position
    键盘打开时给容器设置了bottom:bottom,也就是让容器向上偏移bottom大小距离

到底使用哪一个呢,个人觉得还是看使用场景吧。

问题

如果我们的输入框在一个ScrollView里面,像我们上面写的那个demo一样,而KeyboardAvoidingView在底部加入了paddingBottom,仅仅是压缩了ScrollView的高度,这样本来位置靠下的输入框能显示吗?

结果是可以。

如图所示,ScrollView自动就滚到输入框的位置了。

这也是最令人困惑的地方。同事说可能是react-native内部做了什么处理。希望研究过的同学解答下。

自动滚动

2.android设置textAlign引发的bug

BUG-1

如果同时设置了textAlignheight属性,那么只要手指在TextInput上横向滑动几下,placeholder就不见了,光标也不见了,键盘却能弹出,也能正常输入,但是不显示,用手指再去触摸一下,文字就出来了。。。

ios无此问题。

解决办法:对android不设置高度。

BUG-2

https://github.com/facebook/react-native/issues/12167
https://github.com/facebook/react-native/issues/15764
https://github.com/facebook/react-native/issues/17135

当对TextInput组件设置textAlignright或者center时,当手指触摸到TextInput上方时,外层的ScrollView将中断滚动,而且TextInput会获取到焦点,键盘弹出。当textAlignleft时则不会有此问题。这个问题仅在android手机上会出现。

提出这个问题的人不少,但是没有找到官方的解决方案。

有人指出使用multilinekeyboardType: default 可以解决,尝试了下确实可以,但是实际上我们可能不需要multiline,而且keyboardType为其他类型时,placeholder的文字就会显示了,简直是个迷。。。

还好有人在提出了一个hack写法。

focusInput = () => {
    const { textInput } = this;
    if (!textInput) return;
    if (textInput.isFocused()) {
      textInput.blur();
      setTimeout(() => textInput.focus(), 500);
    } else {
      textInput.focus();
    }
  }
...
<View style={styles.textBox}>
   <TextInput
      style={styles.textInput}
      placeholder={placeholder}
      underlineColorAndroid="transparent"
      autoCorrect={false}
      spellCheck={false}
      placeholderTextColor="#a8afc3"
      selectionColor="#0064ff"
      ref={this.inputRef}
      {...rest}
  />
  <TouchableOpacity
      style={StyleSheet.absoluteFill}
      onPress={this.focusInput}
  />
</View>

原理就是屏蔽了textinput本身的手势,在上面覆盖一个手势遮罩,自己处理textInput的获取焦点和失去焦点。经过尝试,这种方案感觉是可行的,目前还没发有发现明显的问题。

======= 6月20日更新 ========

最新发现了一个问题,由于textinput被覆盖了,导致无法进行长按复制粘贴了=。=||。

于是修改了一些逻辑,即在获取焦点后,让覆盖层消失。

持续更新。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容