Android端KeyboardAwareScrollView与KeyboardAvoidingView布局异常求助
Android端输入框聚焦后布局挤压重叠问题修复方案
当前App在iOS端显示正常,但Android模拟器中输入框聚焦后被挤压至顶部,文本出现重叠。尝试过KeyboardAwareScrollView和KeyboardAvoidingView但未解决问题,核心需求是让聚焦的输入框自动上移至键盘上方。
问题诊断
- 多层
SafeAreaView嵌套导致布局边界计算混乱 KeyboardAwareScrollView设置了extraScrollHeight=-220,负偏移强制内容向上挤压引发重叠- 内容区使用固定百分比高度,键盘弹出时无法灵活适配空间变化
- 绝对定位的顶部导航栏与内容区
paddingTop配合,在键盘弹出时未被正确纳入滚动计算
解决方案
核心修改步骤
- 简化
SafeAreaView结构,仅保留一层包裹整个页面 - 移除
KeyboardAwareScrollView的负偏移参数,启用默认键盘适配逻辑 - 将内容区固定百分比高度改为
flex:1,让布局自动分配剩余空间 - 添加
keyboardShouldPersistTaps='handled'确保点击输入框外区域正常响应 - 调整顶部导航栏定位,确保滚动时保持固定
修改后的完整代码
return ( <SafeAreaView style={{ flex: 1 }}> <StatusBar barStyle="light-content" backgroundColor={theme.colors.primary} /> {/* 顶部固定导航栏 */} <View style={[styles.topBlueContainer, { backgroundColor: theme.colors.primary }]}> {/* Logo */} <View style={styles.logoContainer}> <View style={styles.Viewfe3a5f26}> <Text style={[styles.ConnectLogo, { color: theme.colors['Background'] }]} > {'company'} </Text> <Text style={[styles.WalletLogo, { color: theme.colors['Background'] }]} > {'name'} </Text> <View style={styles.View63f8fbfb}> <Icon name={'AntDesign/trademark'} size={8} color={theme.colors['Background']} /> </View> </View> </View> {/* header row */} <View style={[styles.headerRow, {}]}> <Touchable onPress={() => { navigation.navigate('PortfolioDetailsScreen'); }} style={[styles.backArrowBtnContainer, {}]} > <Icon name={'AntDesign/left'} size={36} color={theme.colors['Background']} /> </Touchable> <View style={[styles.currencyTitleContainer, {}]}> <Text style={[styles.currencyTitleText, { color: theme.colors['Background'] }]}> SEND {selectedToken.symbol}</Text> </View> <View style={styles.backArrowBtnContainer} /> </View> </View> {/* 可滚动内容区 */} <KeyboardAwareScrollView style={{ flex: 1, backgroundColor: theme.colors['Surface'] }} contentContainerStyle={{ flexGrow: 1, paddingHorizontal: 20, alignItems: 'center', paddingBottom: 20 // 避免底部内容被键盘完全遮挡 }} enableOnAndroid={true} resetScrollToCoords={{ x: 0, y: 0 }} keyboardShouldPersistTaps='handled' > {/* Total Amnt Container */} <View style={{ width: '100%', flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Text style={{ fontSize: 56, fontFamily: 'TitilliumWeb_600SemiBold', textAlign: 'center', marginBottom: -10 }}>{selectedToken.balance.toString().slice(0, 10)}</Text> <Text style={{ fontSize: 16, fontFamily: 'TitilliumWeb_400Regular', textAlign: 'center', color: '#979797' }}>Current Balance</Text> </View> {/* Amnt To Send Container */} <View style={{ width: '100%', flex: 1 }}> <View style={{ width: '100%', flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}> <TextInput onChangeText={newTextInputValue => { try { if (newTextInputValue) { if (newTextInputValue === '.') { setAmountToSend('0.'); } else if (parseFloat(newTextInputValue) <= parseFloat(selectedToken.balance)) { setAmountToSend(newTextInputValue); } else { Alert.alert('Not Enough Funds', reachedMaxButtons); } } else { setAmountToSend(newTextInputValue); } } catch (err) { console.error(err); } }} style={[ styles.amountInput, { borderColor: theme.colors.divider }, ]} placeholder={'0.00'} value={amountToSend} editable={true} placeholderTextColor={theme.colors['Medium']} keyboardType={'decimal-pad'} /> </View> <View style={{ width: '100%', flexDirection: 'row', justifyContent: 'space-between', marginTop: 10 }}> <View style={{ width: 100 }}/> <View> <Text style={{ fontSize: 16, fontFamily: 'TitilliumWeb_400Regular', textAlign: 'center', color: '#979797' }}>Amount</Text> </View> <Touchable style={{ width: 100, height: 30, backgroundColor: theme.colors['Background'], borderWidth: 1, borderRadius: 23, borderColor: '#347AF0', alignItems: 'center', justifyContent: 'center' }} onPress={() => { setAmountToSend(selectedToken.balance.toString()); }} > <Text style={{ fontSize: 16, fontFamily: 'TitilliumWeb_600SemiBold', color: '#347AF0' }}>Max</Text> </Touchable> </View> </View> {/* Address Container */} <View style={{ width: '100%', flex: 1 }}> <View style={{ width: '100%', flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}> <TextInput style={styles.addressInput} placeholder={'0x43452...'} placeholderTextColor={theme.colors['Medium']} editable={true} value={addressToSendTo} onChangeText={newAddressValue => { try { setAddressToSendTo(newAddressValue); } catch (err) { console.error(err); } }} /> </View> <View style={{ width: '100%', flexDirection: 'row', justifyContent: 'space-between', marginTop: 10 }}> <View style={{ width: 100 }}/> <View> <Text style={{ fontSize: 16, fontFamily: 'TitilliumWeb_400Regular', textAlign: 'center', color: '#979797' }}>Recipient</Text> </View> <Touchable style={{ width: 100, height: 30, backgroundColor: theme.colors['Background'], borderWidth: 1, borderRadius: 23, borderColor: '#347AF0', alignItems: 'center', justifyContent: 'center' }} onPress={() => { navigation.navigate('CodeScannerScreen', { 'screen': 'SendTokenUpdatedScreen' }); }} > <Text style={{ fontSize: 16, fontFamily: 'TitilliumWeb_600SemiBold', color: '#347AF0' }}>Scan QR</Text> </Touchable> </View> </View> {/* Next Btn Container */} <View style={{ width: '100%', flex: 1, alignItems: 'center', justifyContent: 'flex-start' }}> <Button title={'Next'} style={{ width: '100%', maxWidth: 300, height: 46, fontFamily: 'TitilliumWeb_600SemiBold', fontSize: 19, color: theme.colors['Background'], borderRadius: 23, }} onPress={() => { console.log('amount', amountToSend) console.log('amount there: ', amountToSend === '') navigation.navigate('ConfirmSendTokenScreen', { 'amountToSend': amountToSend, 'addressToSendTo': addressToSendTo, 'selectedToken': selectedToken, }); }} disabled={addressToSendTo === '' || amountToSend === ''} /> </View> </KeyboardAwareScrollView> </SafeAreaView> ); }; const styles = StyleSheet.create({ logoContainer: { paddingLeft: 15, }, Viewfe3a5f26: { alignItems: 'flex-start', flexDirection: 'row', }, WalletLogo: { alignSelf: 'flex-start', fontSize: 28, fontFamily: 'ProximaNova-Bold' }, ConnectLogo: { alignSelf: 'flex-end', fontSize: 28, paddingRight: 5, fontFamily: 'ProximaNova-Regular' }, View63f8fbfb: { paddingTop: 5, }, topBlueContainer: { height: 120, width: '100%' }, headerRow: { width: '100%', flexDirection: 'row', marginTop: 20, }, backArrowBtnContainer: { paddingLeft: 12, justifyContent: 'center', width: '15%', }, currencyTitleContainer: { width: 200, alignItems: 'center', width: '70%' }, currencyTitleText: { fontFamily: 'TitilliumWeb_700Bold', fontSize: 36 }, amountInput: { fontSize: 56, width: '100%', height: 56, textAlign: 'center', borderBottomWidth: 1, fontFamily: 'TitilliumWeb_600SemiBold', }, addressInput: { fontSize: 56, width: '100%', height: 56, marginTop: 10, textAlign: 'center', borderBottomWidth: 1, fontFamily: 'TitilliumWeb_600SemiBold', borderColor: '#CFD2D8' }, });
额外注意事项
- 确保已正确安装并链接
react-native-keyboard-aware-scroll-view库 - Android模拟器需开启"Soft Keyboard"的"Show virtual keyboard"选项,模拟真实设备键盘行为
- 若仍有问题,可添加
keyboardVerticalOffset={120}参数,确保计算键盘位置时避开顶部导航栏
内容的提问来源于stack exchange,提问作者Jake Mulhern
相关产品推荐
相关产品推荐

