《React-Native系列》44、基于多个TextInput的键盘遮挡处理方案优化转

这个方案存在以下问题:

1、移动距离的计算不精确

2、如果表单中有多个TextInput,怎么处理? 

 

今天,我们来给出个比较好的方案。

[javascript] view plain copy 在CODE上查看代码片派生到我的代码片
  1. 'use strict';  
  2. import React, { Component } from 'react';  
  3. import {  
  4.     View,  
  5.     Text,  
  6.     ScrollView,  
  7.     NativeModules,  
  8.     TouchableOpacity,  
  9.     TouchableHighlight,  
  10.     AppRegistry,  
  11.     Image,  
  12.     Platform,  
  13.     TextInput,  
  14.     Dimensions,  
  15.     Keyboard,  
  16. }from 'react-native';  
  17. var dismissKeyboard = require('dismissKeyboard');  
  18. const {width, height} = Dimensions.get('window');  
  19. import styles from '../styles/goods/Spec.css';  
  20.   
  21. export default class Spec extends Component {  
  22.     constructor(props) {  
  23.         super(props);  
  24.         this.state = {  
  25.             groupValueList:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],  
  26.         };  
  27.         this.contentHeight = 0;  
  28.         this.textInputView = null;//当前编辑的textInput  
  29.         this.moveH = 0;//ScrollView滑动的距离  
  30.         this.lastMoveH = 0;//保留上次滑动的距离  
  31.         this.needMove = false;//弹出键盘时,textInputView是否需要滑动  
  32.     }  
  33.   
  34.     componentWillMount(){  
  35.       if (Platform.OS === 'ios') {  
  36.         this.subscriptions = [  
  37.           Keyboard.addListener('keyboardDidShow', this._keyboardDidShow),  
  38.           Keyboard.addListener('keyboardDidHide', this._keyboardDidHide)  
  39.         ];  
  40.       }  
  41.     }  
  42.     componentWillUnmount(){  
  43.       if (Platform.OS === 'ios') {  
  44.         this.subscriptions.forEach((sub) => sub.remove());  
  45.       }  
  46.     }  
  47.     _keyboardDidShow = (e) => {  
  48.       if(! this.textInputView) return ;  
  49.       this.needMove = false;  
  50.       this.refs[this.textInputView].measure((ox, oy, w, h, px, py)=>{  
  51.         let leftHeight = height - py;//输入框距离底部的距离 = (屏幕的高度 - 当前TextInput的高度)  
  52.         //输入框距离底部的距离小于键盘的高度,需要滑动  
  53.         if(  leftHeight < e.startCoordinates.height + 25  ){  
  54.           this.needMove = true;  
  55.           // 需要移动的距离  
  56.           let moveHeight = 30 + (e.startCoordinates.height - leftHeight);  
  57.           console.log("this.moveH=" + this.moveH,"this.contentHeight=" + this.contentHeight,"height=" + height);  
  58.   
  59.           //moveH 异常数据处理  
  60.           if(this.moveH + height > this.contentHeight) {  
  61.             this.moveH = this.contentHeight - height;  
  62.             console.log("===error===");  
  63.           }  
  64.           this.lastMoveH = this.moveH;  
  65.           this.refs.scroll.scrollTo({y:this.moveH + moveHeight ,x:0});  
  66.         }  
  67.       });  
  68.     }  
  69.   
  70.     _keyboardDidHide = () => {  
  71.       if(this.needMove){  
  72.         this.refs.scroll.scrollTo({y:this.lastMoveH ,x:0});  
  73.       }  
  74.       this.textInputView = null;  
  75.     }  
  76.   
  77.     /** 
  78.     * 展示规格项组合项 
  79.     */  
  80.     _renderGroupItem(item , i) {  
  81.       let refId = i;  
  82.       return (  
  83.         <View key={i} style={{      marginLeft:15,borderTopWidth:1,  
  84.               borderTopColor:'#DBDBDB',justifyContent:'center',height:70}}>  
  85.                 <TextInput  
  86.                   ref={ refId }  
  87.                   style={{  
  88.                     width:110,  
  89.                     alignSelf:'center',  
  90.                     borderWidth: 1,  
  91.                     borderColor:'#BDBDBD',  
  92.                     textAlign:'center',  
  93.                     fontSize:13,  
  94.                     color:'#333333',  
  95.                     height:25,  
  96.                   }}  
  97.                   maxLength={5}  
  98.                   placeholderTextColor="#b2b2b2"  
  99.                   placeholder="请输入价格"  
  100.                   multiline={false}  
  101.                   keyboardType="numeric"  
  102.                   returnKeyType={'done'}  
  103.                   value={"" }  
  104.                   onFocus={()=>{this.textInputView = refId}}  
  105.                   />  
  106.         </View>  
  107.       );  
  108.     }  
  109.   
  110.   
  111.     render(){  
  112.         return (  
  113.             <View style={{flex: 1,backgroundColor: '#ffffff'}}>  
  114.               <View/>  
  115.             <ScrollView ref='scroll'  onContentSizeChange ={(contentWidth, contentHeight)=>{  
  116.                 this.contentHeight = parseInt(contentHeight);  
  117.               }}  
  118.             onScrollEndDrag={(e)=>{  
  119.                 this.moveH = e.nativeEvent.contentOffset.y;  
  120.                 // console.log("this.moveH",this.moveH);  
  121.               }}>  
  122.               <View onStartShouldSetResponderCapture={(e)=>{dismissKeyboard();}}>  
  123.                 {  this.state.groupValueList.map((item,i)=>this._renderGroupItem(item,i))   }  
  124.               </View>  
  125.             </ScrollView>  
  126.           </View>  
  127.         );  
  128.     }  
  129. }  
  130. AppRegistry.registerComponent('你的模块',() => Spec);  



代码注释比较详细了,但是这里还是简单说明下以下几个点:

1、只对iOS平台进行处理,RN已经对Android做了键盘的处理

2、当前TextInput的高度

获取到当前正在被编辑的textInput ,this.textInputView 

在键盘弹出监听_keyboardDidShow 中,调用 this.refs[this.textInputView].measure((ox, oy, w, h, px, py)=>{});

此时的py就是当前TextInput在屏幕上的高度
       

3、 输入框距离底部的距离  (leftHeight)

       输入框距离底部的距离 = (屏幕的高度 - 当前TextInput在屏幕上的高度)

      判断leftHeight 是否大于键盘高度,如果大于键盘的高度,默认不处理,如果小于键盘的高度,那么说明TextInput被键盘覆盖

 

4、移动距离的计算

通过ScrollView的onScrollEndDrag方法,计算ScrollView滑动的距离。   this.moveH = e.nativeEvent.contentOffset.y;

通过键盘的高度和leftHeight ,计算出需要移动的距离

 

5、moveH异常数据的处理

当快速滑动的时候,点击TextInput还是会出现移动距离错误的情况,因为ScrollView滑动的距离计算有误

譬如向下滑动到顶部后,继续滑动,this.moveH 会出现负数   ,此情况不需要处理,因为TextInput在键盘上方

向上滑动到底部后,继续滑动,this.moveH会有问题 ,此情况需要处理

解决方案:我们将moveH的数据纠正

如果 moveH + 屏幕的高度 大于 ScrollView内容的高度,说明moveH数值偏高,此时需要将moveH的值纠正为this.contentHeight - height,即:滑动到页面底部的状态。

[javascript] view plain copy 在CODE上查看代码片派生到我的代码片
  1. if(this.moveH + height > this.contentHeight) {  
  2.   this.moveH = this.contentHeight - height;  
  3.   console.log("===error===");  
  4. }  

 

下图可见:moveH数据异常

 

界面展示如下:

 

以上都是基于 RN 0.29.2版本,如果使用其他版本,可能API会有不一致的情况。

posted @ 2017-02-20 18:28  kiydkafa  阅读(1047)  评论(0)    收藏  举报