You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android端KeyboardAwareScrollView与KeyboardAvoidingView布局异常求助

Android端输入框聚焦后布局挤压重叠问题修复方案

当前App在iOS端显示正常,但Android模拟器中输入框聚焦后被挤压至顶部,文本出现重叠。尝试过KeyboardAwareScrollView和KeyboardAvoidingView但未解决问题,核心需求是让聚焦的输入框自动上移至键盘上方。

问题诊断

  1. 多层SafeAreaView嵌套导致布局边界计算混乱
  2. KeyboardAwareScrollView设置了extraScrollHeight=-220,负偏移强制内容向上挤压引发重叠
  3. 内容区使用固定百分比高度,键盘弹出时无法灵活适配空间变化
  4. 绝对定位的顶部导航栏与内容区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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 02:50:53