Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

怎么解决RN TextInput 被键盘遮挡的问题 #39

Open
dwqs opened this issue Oct 14, 2016 · 8 comments
Open

怎么解决RN TextInput 被键盘遮挡的问题 #39

dwqs opened this issue Oct 14, 2016 · 8 comments

Comments

@dwqs
Copy link
Owner

dwqs commented Oct 14, 2016

在0.28版rn中,如果textinput的位置在靠近底部的位置,在textinput获取焦点后,ios上弹出的键盘会遮住textinput,导致用户无法输入;android上弹出键盘时,整个界面会被网上顶,textinput不会被遮住。

在0.28中,解决ios上该问题的方式是利用 ScrollView contentInset 属性,监听键盘的弹出和隐藏事件(keyboardWillShow/keyboardWillHide),获取键盘的高度,动态设置成 contentInset 的值。

将rn升级成0.33后,android和ios上都会出现TextInput 被键盘遮挡的问题,ios上原来的方式也不能解决此问题了。

在 ios 上,textinput 未获取焦点前:

image

获取焦点后:

image

textinput被键盘挡住了。android上有同样的问题。

解决这种情况的一种方式,监听键盘的弹出和隐藏事件,在ScrollView底部设置一个占位符组件,将占位符的高度设置成键盘的高度。

监听键盘事件:

this.keyboardShow = Platform.OS === 'ios' ?
    Keyboard.addListener('keyboardWillShow',this.updateKeyboardSpace.bind(this)) : Keyboard.addListener('keyboardDidShow',this.updateKeyboardSpace.bind(this));
this.keyboardHide = Platform.OS === 'ios' ?
    Keyboard.addListener('keyboardWillHide',this.resetKeyboardSpace.bind(this)) : Keyboard.addListener('keyboardDidHide',this.resetKeyboardSpace.bind(this));

在事件处理程序中,获取键盘高度:

updateKeyboardSpace(frames){
    if(!frames.endCoordinates){
       return;
    }
    let keyboardSpace = frames.endCoordinates.height;//获取键盘高度

    this.setState({
        keyboardSpace: keyboardSpace
    })
}

最后将高度传递给占位符组件:

<ScrollView>
    //其他元素 
    <KeyboardSpacer keyboardSpace={this.state.keyboardSpace}/>
</ScrollView>

KeyboardSpacer组件实现如下:

const styles = StyleSheet.create({
    container: {
        left: 0,
        right: 0,
        bottom: 0
    }
});

export default class KeyboardSpacer extends Component {
    constructor(){
        super();
    }

    static propTypes = {
        keyboardSpace: PropTypes.number
    };

    static defaultProps = {
        keyboardSpace: 0
    };

    render() {

        let {keyboardSpace} = this.props;
        return (
            <View style={[styles.container, { height: ~~keyboardSpace }]} />
        );
    }
}

效果如下:

image

这样子能解决部分场景,例如textInput的位置是靠近底部的。如果textinput框的位置是靠近页面上部的,那么textinput框会被顶上去,就会因超出ScrollView的视口范围而被“遮住”。

这时,就不能单纯的将键盘的高度给 KeyboardSpacer 了,而应该根据textinput在ScrollView视口的位置进行 KeyboardSpacer 高度的计算了。

在页面加载之后,能获取到 ScrollView的视口高度ViewportHeight,这个值是保持不变的。此外,也能拿到textinput距离ScrollViewd顶边的垂直距离InputY,这个值是固定的,不会随着页面的滚动而变化页面示意如下:

1

在界面滚动一定距离之后,页面示意如下:

2

红边表示此时ScrollView的顶边位置,虚线框表示textInput框的原位置,实体框表示textInput在页面滚动之后的位置。

页面滚动时,能获取到ScrollView已经滚动的偏移量 scrollY,此时,就能计算出此时 textInput 距离ScrollView视口底部的距离 InputToBottom:

InputToBottom = ViewportHeight - (InputY - scrollY + textInput的高度)

此时,应该根据InputToBottom与键盘高度的大小比较来设置 keyboardSpace:

keyboardHeight = frames.endCoordinates.height;  //键盘高度
keyboardSpace = InputToBottom >= keyboardHeight ? 0 : keyboardHeight - InputToBottom;

在图示页面的布局中,还有一个底部元素,因而在计算 keyboardHeight 时,因考虑下实际情况是否需要减去底部元素的高度或者ScrollView的marginBottom值。

@ttylikl
Copy link

ttylikl commented Oct 28, 2016

如果有多个TextInput呢?还得先获取一下聚焦的那个吧

@dwqs
Copy link
Owner Author

dwqs commented Oct 30, 2016

多个TextInput 计算的原理是一样的 但是要先获取获得焦点的Textinput的位置 @ttylikl

@SuperMelon
Copy link

如何获取聚焦 textinput的位置呢 ~

@dwqs
Copy link
Owner Author

dwqs commented Dec 30, 2016

监听onlayout事件 @SuperMelon

@SuperMelon
Copy link

谢谢 帮助很大 ~ 多向你学习~

@yanhaijing
Copy link

在下面垫一个元素并不起作用,撑起来了,但是滚动条位置没变,所有还是遮挡

@alooooooone
Copy link

@yanhaijing @dwqs
针对原来在底下之间垫一个键盘高度的元素的方法
如果类似做一个锤子便签的功能,写了一长段文字,然后对中间部分进行修改。由于滚动位置不变导致键盘还是遮挡到了文字,虽然可以手动往下拉

我觉得应该是要能获取到光标的位置信息
然后结合光标的位置来设置scrollTo

现在卡在了获取光标信息这一块,不知道有没有什么解决办法

@Catlc
Copy link

Catlc commented Jun 1, 2018

@jielongink 你解决了么

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants