Skip to content

ScrollView(滚动组件)

React Native 核心基础

在 React Native 中,ScrollView 是用于创建可滚动视图的核心组件。它允许用户在内容超出屏幕尺寸时滚动查看。

1. 什么是 ScrollView 组件?

ScrollView 组件是 React Native 中用于创建可滚动视图的基本组件。它支持垂直和水平滚动,以及各种滚动相关的功能。

主要功能

  • 垂直滚动:允许内容垂直滚动
  • 水平滚动:允许内容水平滚动
  • 滚动条:支持显示或隐藏滚动条
  • 滚动事件:支持滚动开始、滚动中、滚动结束等事件
  • 性能优化:支持延迟加载和虚拟化(通过 FlatList 等)

2. 基本用法

垂直滚动

jsx
import { ScrollView, View, Text, StyleSheet } from 'react-native';

export default function VerticalScrollView() {
  return (
    <ScrollView style={styles.container}>
      {Array.from({ length: 50 }).map((_, index) => (
        <View key={index} style={styles.item}>
          <Text>Item {index + 1}</Text>
        </View>
      ))}
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  item: {
    height: 50,
    backgroundColor: '#f0f0f0',
    marginVertical: 5,
    marginHorizontal: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

水平滚动

jsx
import { ScrollView, View, Text, StyleSheet } from 'react-native';

export default function HorizontalScrollView() {
  return (
    <ScrollView 
      style={styles.container}
      horizontal
      showsHorizontalScrollIndicator={false}
    >
      {Array.from({ length: 20 }).map((_, index) => (
        <View key={index} style={styles.item}>
          <Text>Item {index + 1}</Text>
        </View>
      ))}
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    height: 100,
  },
  item: {
    width: 100,
    height: 80,
    backgroundColor: '#f0f0f0',
    marginHorizontal: 5,
    marginVertical: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

带滚动事件的 ScrollView

jsx
import { ScrollView, View, Text, StyleSheet } from 'react-native';
import { useState } from 'react';

export default function ScrollViewWithEvents() {
  const [scrollY, setScrollY] = useState(0);

  const handleScroll = (event) => {
    setScrollY(event.nativeEvent.contentOffset.y);
  };

  return (
    <View style={styles.container}>
      <Text style={styles.scrollInfo}>Scroll position: {Math.round(scrollY)}</Text>
      <ScrollView 
        style={styles.scrollView}
        onScroll={handleScroll}
        scrollEventThrottle={16} // 约 60fps
      >
        {Array.from({ length: 50 }).map((_, index) => (
          <View key={index} style={styles.item}>
            <Text>Item {index + 1}</Text>
          </View>
        ))}
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  scrollInfo: {
    padding: 10,
    backgroundColor: '#f0f0f0',
    textAlign: 'center',
  },
  scrollView: {
    flex: 1,
  },
  item: {
    height: 50,
    backgroundColor: '#f0f0f0',
    marginVertical: 5,
    marginHorizontal: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

3. 常用属性

核心属性

属性描述示例
horizontal是否水平滚动horizontal={true}
showsVerticalScrollIndicator是否显示垂直滚动条showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator是否显示水平滚动条showsHorizontalScrollIndicator={false}
scrollEventThrottle滚动事件触发频率scrollEventThrottle={16}
bounces是否允许反弹效果bounces={false}
scrollEnabled是否允许滚动scrollEnabled={true}
pagingEnabled是否启用分页滚动pagingEnabled={true}
contentContainerStyle内容容器样式contentContainerStyle={ { padding: 20 } }
style样式对象style={styles.scrollView}

事件属性

属性描述示例
onScroll滚动时触发onScroll={(event) => console.log(event.nativeEvent.contentOffset.y)}
onScrollBeginDrag开始拖动时触发onScrollBeginDrag={() => console.log('Scroll began')}
onScrollEndDrag结束拖动时触发onScrollEndDrag={() => console.log('Scroll ended')}
onMomentumScrollBegin动量滚动开始时触发onMomentumScrollBegin={() => console.log('Momentum scroll began')}
onMomentumScrollEnd动量滚动结束时触发onMomentumScrollEnd={() => console.log('Momentum scroll ended')}

4. 高级用法

嵌套 ScrollView

jsx
import { ScrollView, View, Text, StyleSheet } from 'react-native';

export default function NestedScrollView() {
  return (
    <ScrollView style={styles.verticalScrollView}>
      <Text style={styles.title}>Vertical Scroll View</Text>
      
      <View style={styles.horizontalSection}>
        <Text style={styles.subtitle}>Horizontal Scroll View</Text>
        <ScrollView 
          horizontal
          showsHorizontalScrollIndicator={false}
          style={styles.horizontalScrollView}
        >
          {Array.from({ length: 10 }).map((_, index) => (
            <View key={index} style={styles.horizontalItem}>
              <Text>Item {index + 1}</Text>
            </View>
          ))}
        </ScrollView>
      </View>
      
      {Array.from({ length: 30 }).map((_, index) => (
        <View key={index} style={styles.verticalItem}>
          <Text>Vertical Item {index + 1}</Text>
        </View>
      ))}
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  verticalScrollView: {
    flex: 1,
  },
  title: {
    fontSize: 20,
    fontWeight: 'bold',
    padding: 20,
  },
  horizontalSection: {
    marginBottom: 20,
  },
  subtitle: {
    fontSize: 16,
    marginLeft: 20,
    marginBottom: 10,
  },
  horizontalScrollView: {
    height: 100,
  },
  horizontalItem: {
    width: 100,
    height: 80,
    backgroundColor: '#f0f0f0',
    marginHorizontal: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
  verticalItem: {
    height: 50,
    backgroundColor: '#f0f0f0',
    marginVertical: 5,
    marginHorizontal: 20,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

带刷新功能的 ScrollView

jsx
import { ScrollView, View, Text, StyleSheet, RefreshControl } from 'react-native';
import { useState } from 'react';

export default function ScrollViewWithRefresh() {
  const [refreshing, setRefreshing] = useState(false);
  const [data, setData] = useState(Array.from({ length: 20 }, (_, i) => i + 1));

  const onRefresh = () => {
    setRefreshing(true);
    // 模拟网络请求
    setTimeout(() => {
      // 生成新数据
      const newData = Array.from({ length: 20 }, () => Math.floor(Math.random() * 100));
      setData(newData);
      setRefreshing(false);
    }, 1500);
  };

  return (
    <ScrollView
      style={styles.container}
      refreshControl={
        <RefreshControl
          refreshing={refreshing}
          onRefresh={onRefresh}
          colors={['#007AFF']} // iOS
          tintColor="#007AFF" // Android
        />
      }
    >
      {data.map((item, index) => (
        <View key={index} style={styles.item}>
          <Text>Item {item}</Text>
        </View>
      ))}
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  item: {
    height: 50,
    backgroundColor: '#f0f0f0',
    marginVertical: 5,
    marginHorizontal: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

无限滚动

jsx
import { ScrollView, View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import { useState, useCallback } from 'react';

export default function InfiniteScrollView() {
  const [data, setData] = useState(Array.from({ length: 20 }, (_, i) => i + 1));
  const [loading, setLoading] = useState(false);

  const loadMore = useCallback(() => {
    if (loading) return;
    
    setLoading(true);
    // 模拟网络请求
    setTimeout(() => {
      const newData = Array.from({ length: 10 }, (_, i) => data.length + i + 1);
      setData([...data, ...newData]);
      setLoading(false);
    }, 1500);
  }, [data, loading]);

  const handleScroll = (event) => {
    const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent;
    const paddingToBottom = 20;
    if (
      layoutMeasurement.height + contentOffset.y >=
      contentSize.height - paddingToBottom
    ) {
      loadMore();
    }
  };

  return (
    <ScrollView
      style={styles.container}
      onScroll={handleScroll}
      scrollEventThrottle={16}
    >
      {data.map((item, index) => (
        <View key={index} style={styles.item}>
          <Text>Item {item}</Text>
        </View>
      ))}
      {loading && (
        <View style={styles.loadingContainer}>
          <ActivityIndicator size="large" color="#007AFF" />
        </View>
      )}
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  item: {
    height: 50,
    backgroundColor: '#f0f0f0',
    marginVertical: 5,
    marginHorizontal: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
  loadingContainer: {
    padding: 20,
    alignItems: 'center',
  },
});

5. 性能优化

1. 使用 FlatList 替代 ScrollView

对于长列表,使用 FlatList 替代 ScrollView,因为 FlatList 支持虚拟化,只渲染可见的项目。

jsx
import { FlatList, View, Text, StyleSheet } from 'react-native';

export default function FlatListExample() {
  const data = Array.from({ length: 1000 }, (_, i) => ({ id: i, value: i + 1 }));

  const renderItem = ({ item }) => (
    <View style={styles.item}>
      <Text>Item {item.value}</Text>
    </View>
  );

  return (
    <FlatList
      data={data}
      renderItem={renderItem}
      keyExtractor={(item) => item.id.toString()}
      contentContainerStyle={styles.container}
    />
  );
}

const styles = StyleSheet.create({
  container: {
    paddingVertical: 10,
  },
  item: {
    height: 50,
    backgroundColor: '#f0f0f0',
    marginVertical: 5,
    marginHorizontal: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

2. 避免在 ScrollView 中使用大量复杂组件

  • 尽量减少 ScrollView 中的组件数量
  • 避免在 ScrollView 中使用复杂的计算或渲染逻辑
  • 对于复杂的内容,考虑使用懒加载

3. 优化滚动事件处理

  • 使用 scrollEventThrottle 控制滚动事件的触发频率
  • 避免在 onScroll 回调中执行复杂的计算
  • 考虑使用 useNativeDriver 进行动画
jsx
import { ScrollView, View, Text, StyleSheet, Animated } from 'react-native';
import { useRef } from 'react';

export default function OptimizedScrollView() {
  const scrollY = useRef(new Animated.Value(0)).current;

  const handleScroll = Animated.event(
    [{ nativeEvent: { contentOffset: { y: scrollY } } }],
    { useNativeDriver: true }
  );

  const opacity = scrollY.interpolate({
    inputRange: [0, 100],
    outputRange: [1, 0],
    extrapolate: 'clamp',
  });

  return (
    <ScrollView 
      style={styles.container}
      onScroll={handleScroll}
      scrollEventThrottle={16}
    >
      <Animated.View style={[styles.header, { opacity }]}>
        <Text style={styles.headerText}>Header</Text>
      </Animated.View>
      
      {Array.from({ length: 50 }).map((_, index) => (
        <View key={index} style={styles.item}>
          <Text>Item {index + 1}</Text>
        </View>
      ))}
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  header: {
    height: 100,
    backgroundColor: '#007AFF',
    alignItems: 'center',
    justifyContent: 'center',
  },
  headerText: {
    color: 'white',
    fontSize: 20,
    fontWeight: 'bold',
  },
  item: {
    height: 50,
    backgroundColor: '#f0f0f0',
    marginVertical: 5,
    marginHorizontal: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

6. 常见错误与解决方案

错误 1:ScrollView 不滚动

错误

jsx
import { ScrollView, View, Text, StyleSheet } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <ScrollView>
        {Array.from({ length: 50 }).map((_, index) => (
          <View key={index} style={styles.item}>
            <Text>Item {index + 1}</Text>
          </View>
        ))}
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  item: {
    // 错误:没有设置高度,内容可能不足以滚动
  },
});

解决方案

jsx
import { ScrollView, View, Text, StyleSheet } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <ScrollView>
        {Array.from({ length: 50 }).map((_, index) => (
          <View key={index} style={styles.item}>
            <Text>Item {index + 1}</Text>
          </View>
        ))}
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  item: {
    height: 50, // 正确:设置了高度,确保内容超出屏幕
    backgroundColor: '#f0f0f0',
    marginVertical: 5,
    marginHorizontal: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

错误 2:水平 ScrollView 不显示

错误

jsx
import { ScrollView, View, Text, StyleSheet } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <ScrollView horizontal>
        {Array.from({ length: 20 }).map((_, index) => (
          <View key={index} style={styles.item}>
            <Text>Item {index + 1}</Text>
          </View>
        ))}
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  item: {
    width: 100,
    // 错误:没有设置高度
  },
});

解决方案

jsx
import { ScrollView, View, Text, StyleSheet } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <ScrollView 
        horizontal
        style={styles.scrollView}
      >
        {Array.from({ length: 20 }).map((_, index) => (
          <View key={index} style={styles.item}>
            <Text>Item {index + 1}</Text>
          </View>
        ))}
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  scrollView: {
    height: 100, // 正确:设置了 ScrollView 的高度
  },
  item: {
    width: 100,
    height: 80, // 正确:设置了项目的高度
    backgroundColor: '#f0f0f0',
    marginHorizontal: 5,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

错误 3:滚动性能问题

错误:在 ScrollView 中渲染大量复杂组件,导致滚动卡顿

解决方案

  • 使用 FlatList 替代 ScrollView
  • 优化组件渲染
  • 减少滚动事件中的计算

7. 最佳实践

1. 选择合适的滚动组件

  • 对于长列表,使用 FlatListSectionList
  • 对于短内容或需要混合滚动的场景,使用 ScrollView

2. 性能优化

  • 避免在 ScrollView 中渲染大量组件
  • 使用 scrollEventThrottle 控制滚动事件频率
  • 对于复杂的滚动效果,使用 Animated API 并启用 useNativeDriver

3. 用户体验

  • 为长列表添加下拉刷新功能
  • 为长列表添加加载更多功能
  • 合理设置滚动条的显示/隐藏

4. 布局考虑

  • 为 ScrollView 设置适当的容器大小
  • 对于水平 ScrollView,确保设置了高度
  • 对于垂直 ScrollView,确保内容足够长以触发滚动

8. 总结

ScrollView 是 React Native 中用于创建可滚动视图的核心组件。通过合理使用 ScrollView 组件,你可以创建出各种滚动界面。

在使用 ScrollView 组件时,要注意:

  1. 为 ScrollView 设置适当的容器大小
  2. 对于长列表,考虑使用 FlatList 替代 ScrollView
  3. 优化滚动事件处理,避免卡顿
  4. 为用户提供良好的滚动体验,如下拉刷新和加载更多

掌握 ScrollView 组件的使用,是创建流畅、响应式 React Native 应用的基础。在接下来的教程中,我们将学习更多的核心组件,如 ButtonTouchable 系列等。

© 2026 编程马·菜鸟教程 版权所有