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

React Native横向ScrollView无法滚动问题求助

横向ScrollView无法滚动的解决方案

问题原因

你的代码里有两个关键问题导致横向ScrollView无法滚动:

  • 分类按钮(categoriesBtn/categoriesBtnActive)设置了flex:1,在横向ScrollView中,这个属性会让每个按钮均分ScrollView的可用宽度,所有按钮被压缩到可视区域内,没有超出内容,自然无法触发滚动。
  • SafeAreaView设置了flex:1,但父容器container已经固定了height:130,可能导致ScrollView的高度被挤压,进一步影响滚动交互。

修复步骤

  1. 移除按钮的flex:1属性:让按钮根据自身内容自适应宽度,这样多个按钮排列后会超出可视区域,触发滚动。
  2. 调整SafeAreaView的样式:去掉flex:1,避免影响固定高度的容器布局。
  3. 优化按钮间距:在scrollViewContent中添加gap属性,统一控制按钮之间的间距,代替每个按钮的paddingHorizontal,布局更整洁。

修改后的完整代码

import React, { useRef, useState } from 'react';
import { View, Text, SafeAreaView, StyleSheet, ScrollView, TouchableOpacity } from 'react-native';
import Colors from '@/constants/Colors';
import { Ionicons, MaterialIcons } from '@expo/vector-icons';
import * as Haptics from 'expo-haptics';
import { Link } from 'expo-router';

const categories = [
  { name: 'Tiny homes', icon: 'home' },
  { name: 'Cabins', icon: 'house-siding' },
  { name: 'Trending', icon: 'local-fire-department' },
  { name: 'Play', icon: 'videogame-asset' },
  { name: 'City', icon: 'apartment' },
  { name: 'Beachfront', icon: 'beach-access' },
  { name: 'Countryside', icon: 'nature-people' },
];

interface Props {
  onCategoryChanged: (category: string) => void;
}

const ExploreHeader = ({ onCategoryChanged }: Props) => {
  const scrollRef = useRef<ScrollView>(null);
  const itemsRef = useRef<Array<TouchableOpacity | null>>([]);
  const [activeIndex, setActiveIndex] = useState(0);

  const selectCategory = (index: number) => {
    const selected = itemsRef.current[index];
    setActiveIndex(index);
    selected?.measure((x) => {
      scrollRef.current?.scrollTo({ x: x - 16, y: 0, animated: true });
    });
    Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
    onCategoryChanged(categories[index].name);
  };

  return (
    <SafeAreaView style={{ backgroundColor: '#fff' }}>
      <View style={styles.container}>
        <View style={styles.actionRow}>
          <Link href={'/(modals)/booking'} asChild>
            <TouchableOpacity>
              <View style={styles.searchBtn}>
                <Ionicons name="search" size={24} />
                <View>
                  <Text style={{ fontFamily: 'mon-sb' }}>Where to?</Text>
                  <Text style={{ color: Colors.grey, fontFamily: 'mon' }}>Anywhere · Any week</Text>
                </View>
              </View>
            </TouchableOpacity>
          </Link>
          <TouchableOpacity style={styles.filterBtn}>
            <Ionicons name="options-outline" size={24} />
          </TouchableOpacity>
        </View>

        <ScrollView
          horizontal
          ref={scrollRef}
          showsHorizontalScrollIndicator={false}
          contentContainerStyle={styles.scrollViewContent}>
          {categories.map((item, index) => (
            <TouchableOpacity
              ref={(el) => (itemsRef.current[index] = el)}
              key={index}
              style={activeIndex === index ? styles.categoriesBtnActive : styles.categoriesBtn}
              onPress={() => selectCategory(index)}>
              <MaterialIcons
                name={item.icon as any}
                size={24}
                color={activeIndex === index ? '#000' : Colors.grey}
              />
              <Text style={activeIndex === index ? styles.categoryTextActive : styles.categoryText}>
                {item.name}
              </Text>
            </TouchableOpacity>
          ))}
        </ScrollView>
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#fff',
    height: 130,
    elevation: 2,
    shadowColor: '#000',
    shadowOpacity: 0.1,
    shadowRadius: 6,
    shadowOffset: {
      width: 1,
      height: 10,
    },
  },
  actionRow: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    paddingHorizontal: 24,
    paddingBottom: 16,
  },
  searchBtn: {
    backgroundColor: '#fff',
    flexDirection: 'row',
    gap: 10,
    padding: 14,
    alignItems: 'center',
    width: 280,
    borderWidth: StyleSheet.hairlineWidth,
    borderColor: '#c2c2c2',
    borderRadius: 30,
    elevation: 2,
    shadowColor: '#000',
    shadowOpacity: 0.12,
    shadowRadius: 8,
    shadowOffset: {
      width: 1,
      height: 1,
    },
  },
  filterBtn: {
    padding: 10,
    borderWidth: 1,
    borderColor: '#A2A0A2',
    borderRadius: 24,
  },
  categoryText: {
    fontSize: 14,
    fontFamily: 'mon-sb',
    color: Colors.grey,
  },
  categoryTextActive: {
    fontSize: 14,
    fontFamily: 'mon-sb',
    color: '#000',
  },
  categoriesBtn: {
    alignItems: 'center',
    justifyContent: 'center',
    paddingBottom: 8,
    paddingHorizontal: 16,
  },
  categoriesBtnActive: {
    alignItems: 'center',
    justifyContent: 'center',
    borderBottomColor: '#000',
    borderBottomWidth: 2,
    paddingBottom: 8,
    paddingHorizontal: 16,
  },
  scrollViewContent: {
    alignItems: 'center',
    paddingHorizontal:16,
    gap: 12,
  },
});

export default ExploreHeader;

额外说明

  • 移除flex:1后,按钮会根据文字和图标的宽度自适应,多个按钮排列后超出ScrollView的可视宽度,就能正常触发横向滚动。
  • 如果滚动依然不生效,可以检查container的height是否足够,确保ScrollView有足够的显示空间;也可以给ScrollView显式设置height属性(比如height: 60)来测试。

内容的提问来源于stack exchange,提问作者Adhayan Sharma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 13:29:59