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

已解决:React Native中ScrollView聚焦输入框时滚动过度问题

解决React Native ScrollView聚焦输入框后滚动位置过高的问题

问题描述

点击输入框后,ScrollView会将输入框滚动到导航栏上方的位置,需求是让聚焦后的输入框刚好位于导航栏下方。

核心原因

当前ScrollView的自动键盘适配未考虑导航栏高度,导致滚动偏移量计算错误。以下是几种针对性的解决方案:


方案1:使用KeyboardAvoidingView适配键盘与导航栏

这是最直接的官方解决方案,通过包裹ScrollView并设置对应导航栏高度的偏移量,让系统自动调整滚动位置。

修改wrapper.js:

import React, { useState, useCallback } from 'react';
import { ScrollView, View, KeyboardAvoidingView, RefreshControl, Platform } from 'react-native';
import {styles} from '../assets/styles/components/Wrapper' 

export default function Wrapper(props){
    const [refreshing, setRefreshing] = useState(false);
    
    const onRefresh = useCallback(() => {
      setRefreshing(true);
      setTimeout(() => {
        setRefreshing(false);
      }, 2000);
    }, []);
 
    return (
      {/* 用KeyboardAvoidingView包裹ScrollView */}
      <KeyboardAvoidingView
        behavior={Platform.OS === "ios" ? "padding" : "height"}
        // 偏移量与导航栏高度一致(WrapperWithNavbar中设置的paddingTop为110)
        keyboardVerticalOffset={Platform.OS === "ios" ? 110 : 0}
        style={{ flex: 1 }}
      >
        <ScrollView
          automaticallyAdjustKeyboardInsets={true}        
          refreshControl={
              <RefreshControl refreshing={props.refreshing || false} onRefresh={props.onRefresh} />
          }
          style={[styles.wrapper, props.style]}
        >
          <View style={[styles.wrapperInner, props.innerStyle]}>
            {props.children}
          </View>
        </ScrollView>
      </KeyboardAvoidingView>
    )
}

方案2:手动控制输入框聚焦时的滚动位置

如果方案1无法满足需求,可以通过测量输入框位置,手动调用ScrollView的滚动方法,精准控制滚动位置。

步骤1:修改wrapper.js,暴露ScrollView的ref

import React, { useState, useCallback, useRef } from 'react';
import { ScrollView, View, RefreshControl } from 'react-native';
import {styles} from '../assets/styles/components/Wrapper' 

export default function Wrapper(props){
    const scrollViewRef = useRef(null);
    const [refreshing, setRefreshing] = useState(false);
    
    const onRefresh = useCallback(() => {
      setRefreshing(true);
      setTimeout(() => {
        setRefreshing(false);
      }, 2000);
    }, []);

    // 提供滚动到指定位置的方法给父组件
    const scrollToPosition = (y) => {
      scrollViewRef.current?.scrollTo({ y, animated: true });
    }
 
    return (
      <ScrollView
        ref={scrollViewRef}
        automaticallyAdjustKeyboardInsets={true}        
        refreshControl={
            <RefreshControl refreshing={props.refreshing || false} onRefresh={props.onRefresh} />
        }
        style={[styles.wrapper, props.style]}
      >
        <View style={[styles.wrapperInner, props.innerStyle]}>
          {/* 将滚动方法传递给子元素 */}
          {React.cloneElement(props.children, { scrollToPosition })}
        </View>
      </ScrollView>
    )
}

步骤2:修改输入框页面,处理聚焦事件

import React, { useState, useRef } from 'react';
import { Text, View, Platform } from 'react-native';
import {useNavigation} from '@react-navigation/native';
 
// components
import WrapperWithNavbar from '../../components/WrapperWithNavbar' 
import Header from '../../components/Header'
import Button from '../../components/Button'
import Input from '../../components/Input'

export default function CreateEventCultureplace(props){
  const navigation = useNavigation();
  const [event, setEvent] = useState('');
  const inputRef = useRef(null);
  const { scrollToPosition } = props;

  const handleInputFocus = () => {
    inputRef.current?.measure((_x, _y, _width, _height, _pageX, pageY) => {
      // 计算滚动位置:输入框的页面顶部位置 - 导航栏高度
      const targetY = pageY - 110;
      scrollToPosition(targetY);
    });
  }
   
  return (
    <WrapperWithNavbar type="CULTUREPLACE">
      <Header title="Opret begivenhed"/>
      <Input 
        ref={inputRef}
        value={event} 
        onChangeText={setEvent} 
        type="text" 
        title="Begivenhed (f.eks. open mic)"
        onFocus={handleInputFocus}
      />
      <Button onPress={()=>{allowPress && createEvent()}} style={[!allowPress?{backgroundColor:"#ffb38a"}:null,{"marginBottom":50}]} title="Opret begivenhed"/>
      {Platform.OS!="web" && <View style={{"height":100}}></View>}
    </WrapperWithNavbar>
  )
}

步骤3:修改input.js,支持ref传递

import React, { useState, forwardRef } from 'react';
import {SafeAreaView, Text, TextInput, Platform} from 'react-native';
import {styles} from '../assets/styles/components/Input'

// 使用forwardRef让父组件可以获取TextInput的ref
const Input = forwardRef((props, ref) => {
    const [isFocused, setIsFocused] = useState(false);
    
    return (
        <SafeAreaView style={[props.style,styles.wrapper]}>
            <Text style={styles.text}>{props.title}</Text>
            <TextInput
                ref={ref}
                scrollEnabled={false}
                numberOfLines={props.numberOfLines}
                multiline={props.multiline === true}
                onKeyPress={props.onKeyPress}
                onFocus={() => {
                  setIsFocused(true);
                  props.onFocus?.();
                }}
                onBlur={() => setIsFocused(false)}
                autofocus={props.autofocus || false}
                style={[styles.input, props.inputStyle, isFocused && !props.readonly ? styles.focused : '', Platform.OS=='web' && styles.web]}
                value={props.value}
                onChangeText={props.onChangeText}
                placeholder={props.title}
                placeholderTextColor="#777"
                autocomplete={props.autocomplete}
                secureTextEntry={props.type === "password"}
                editable={!props.readonly}
            />
        </SafeAreaView>
    );
})

export default Input;

方案3:调整ScrollView的内容内边距

通过设置contentInset和scrollIndicatorInsets,让ScrollView的内容默认留出导航栏高度的空间,避免滚动过度:

修改wrapper.js中的ScrollView:

<ScrollView
  automaticallyAdjustKeyboardInsets={true}        
  refreshControl={
      <RefreshControl refreshing={props.refreshing || false} onRefresh={props.onRefresh} />
  }
  style={[styles.wrapper, props.style]}
  // 设置内容内边距,顶部与导航栏高度一致
  contentInset={{ top: Platform.OS === 'ios' ? 110 : 0 }}
  scrollIndicatorInsets={{ top: Platform.OS === 'ios' ? 110 : 0 }}
>
  {/* ...子元素 */}
</ScrollView>

推荐方案

优先选择方案1,因为KeyboardAvoidingView是React Native官方提供的键盘适配组件,配置简单且兼容性好,只需确保keyboardVerticalOffset的值与导航栏实际高度匹配即可。

内容的提问来源于stack exchange,提问作者Lukas Guldstøv

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 14:24:51