【React Native】ScrollView 和 FlatList 组件

ScrollView 组件

基础使用

复制代码
import { View, Text, StyleSheet } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Text style={styles.content}>
        君不见黄河之水天上来,奔流到海不复回。
        君不见高堂明镜悲白发,朝如青丝暮成雪。
        人生得意须尽欢,莫使金樽空对月。
        天生我材必有用,千金散尽还复来。
        烹羊宰牛且为乐,会须一饮三百杯。
        岑夫子,丹丘生,将进酒,杯莫停。
        与君歌一曲,请君为我倾耳听。
        钟鼓馔玉不足贵,但愿长醉不愿醒。
        古来圣贤皆寂寞,惟有饮者留其名。
        陈王昔时宴平乐,斗酒十千恣欢谑。
        主人何为言少钱,径须沽取对君酌。
        五花马、千金裘,
        呼儿将出换美酒,与尔同销万古愁。
      </Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  },
  content: {
    fontSize: 60
  }
});

可以看到字体很大的情况下,文本溢出,但是不支持滚动(因为View组件不支持滚动)

但是此时将 View 组件改为 ScrollView 组件,会发现可以正常滚动:

复制代码
<ScrollView style={styles.container}>
    // ...
</ScrollView>

ScrollView经常会作为一个页面里最大的容器,里面再嵌套View,和其他各种组件。

SafeAreaView 处理刘海屏问题

如果大家用的是iOS设备,会发现文字到最顶上去了,被iPhone的刘海给挡住了。如果用Android倒是没有这个问题。解决方案是使用SafeAreaView的组件。

ScrollView的外面,包上一层SafeAreaView,并且把ScrollView的样式,挪到SafeAreaView上来。

复制代码
export default function App() {
  return (
    <SafeAreaView style={styles.container}>
      <ScrollView>
        // ...
      </ScrollView>
    </SafeAreaView>
  );
};

RefreshControl 实现下拉刷新

ScrollView经常还会搭配RefreshControl组件来使用,这样可以实现下拉刷新。

复制代码
import { useState } from "react";
import {
  View,
  Text,
  StyleSheet,
  ScrollView,
  SafeAreaView,
  RefreshControl,
} from "react-native";

export default function App() {
  const [refreshing, setRefreshing] = useState(false);

  const onRefresh = () => {
    setRefreshing(true);

    // 模拟重新读取接口
    console.log("开始读取接口了");
    setTimeout(() => {
      setRefreshing(false);
    }, 2000);
  };
  return (
    <SafeAreaView style={styles.container}>
      <ScrollView
        refreshControl={
          <RefreshControl
            refreshing={refreshing}
            onRefresh={onRefresh}
            tintColor={"#1f99b0"}
          />
        }
      >
        <Text style={styles.content}>
          君不见黄河之水天上来,奔流到海不复回。
          君不见高堂明镜悲白发,朝如青丝暮成雪。
          人生得意须尽欢,莫使金樽空对月。 天生我材必有用,千金散尽还复来。
          烹羊宰牛且为乐,会须一饮三百杯。 岑夫子,丹丘生,将进酒,杯莫停。
          与君歌一曲,请君为我倾耳听。 钟鼓馔玉不足贵,但愿长醉不愿醒。
          古来圣贤皆寂寞,惟有饮者留其名。 陈王昔时宴平乐,斗酒十千恣欢谑。
          主人何为言少钱,径须沽取对君酌。 五花马、千金裘,
          呼儿将出换美酒,与尔同销万古愁。
        </Text>
      </ScrollView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
  },
  content: {
    fontSize: 60,
  },
});

配置了RefreshControl,并把这几个参数传进去:

  • refreshing,控制是否显示加载中指示器。
  • onRefresh,表示当用户下拉了,要执行什么函数。
  • 最后一个tintColor参数是加载中指示器的颜色,注意下,这个属性值只在iOS上有效,Android无效。

FlatList 组件

FlatList 组件用于处理长列表渲染。

以下这种直接渲染在数据量少的情况下可以正常使用,但是一旦数据量非常大,map 会遍历数组,然后一次性全部渲染出来。无论这个元素是否在屏幕的可见区域内,这样就会造成了大量的性能浪费。

复制代码
import { Text, ScrollView, StyleSheet } from 'react-native';
import { SafeAreaView } from 'react-native';

export default function App() {
  const data = [
    { "id": 1, "title": "静夜思" },
    { "id": 2, "title": "望庐山瀑布" },
    { "id": 3, "title": "早发白帝城" },
    { "id": 4, "title": "黄鹤楼送孟浩然之广陵" },
    { "id": 5, "title": "将进酒" },
    { "id": 6, "title": "行路难·其一" },
    { "id": 7, "title": "蜀道难" },
    { "id": 8, "title": "月下独酌·其一" },
    { "id": 9, "title": "赠汪伦" },
    { "id": 10, "title": "梦游天姥吟留别" },
    { "id": 11, "title": "宣州谢朓楼饯别校书叔云" },
    { "id": 12, "title": "送友人" },
    { "id": 13, "title": "登金陵凤凰台" },
    { "id": 14, "title": "清平调·其一" },
    { "id": 15, "title": "秋浦歌·白发三千丈" },
    { "id": 16, "title": "渡荆门送别" },
    { "id": 17, "title": "夜宿山寺" },
    { "id": 18, "title": "独坐敬亭山" },
    { "id": 19, "title": "关山月" },
    { "id": 20, "title": "子夜吴歌·秋歌" },
    { "id": 21, "title": "下终南山过斛斯山人宿置酒" },
    { "id": 22, "title": "月下独酌·其二" },
    { "id": 23, "title": "塞下曲六首·其一" },
    { "id": 24, "title": "玉阶怨" },
    { "id": 25, "title": "春夜洛城闻笛" },
    { "id": 26, "title": "越中览古" },
    { "id": 27, "title": "山中问答" },
    { "id": 28, "title": "清平调·其一" },
    { "id": 29, "title": "清平调·其二" },
    { "id": 30, "title": "清平调·其三" }
  ]

  return (
    <SafeAreaView style={styles.container}>
      <ScrollView>
          {data.map((item) => (
            <Text key={item.id} style={styles.title}>
              {item.title}
            </Text>
          ))}
      </ScrollView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  },
  header: {
    textAlign: 'center',
    fontSize: 50,
    lineHeight: 60,
    fontWeight: 'bold',
  },
  footer: {
    textAlign: 'center',
    fontSize: 30,
    lineHeight: 60,
    color: '#999',
  },
  title: {
    textAlign: 'center',
    fontSize: 30,
    lineHeight: 60,
  }
});

更好的办法是使用FlatList,它是一个专门用来渲染长列表的组件,用它可以实现按需渲染。也就说只渲染当前屏幕可见的元素。当你滚动时,就动态加载新元素,回收不可见的元素。这样就只有比较小的性能开销了。

复制代码
export default function App() {
  // ...

    return (
    <SafeAreaView style={styles.container}>
      <FlatList
        data={data}
        renderItem={({ item }) => <Text style={styles.title}>{item.title}</Text>}
        keyExtractor={item => item.id}
      />
    </SafeAreaView>
  );
};
  • 它里面接受一个data参数,这就是要遍历的数据了。
  • renderItem,就是要遍历渲染出来的内容。
  • keyExtractor,与map里的key是一回事。
  • ListHeaderComponentListFooterComponent,可以在同一个页面里,顶部和底部渲染其他组件。

还有个特别要注意的事情,如果在FlatList外面加了一层ScrollView,应用会直接报错。

提示我们,不要把相同方向的虚拟列表,放到ScrollView里。这也就是说,ScrollView是上下滚动的,FlatList也是上下滚动的,这两个之间是有冲突的。所以这里有了FlatList了,它自己就可以实现滚动,就不要再额外加上ScrollView了。

但是如果FlatList不是上下滚动,而是横向的左右滚动,与ScrollView方向不同,就不会出现这个报错。

复制代码
export default function App() {
  // ...

    return (
    <SafeAreaView style={styles.container}>
      <ScrollView>
        <FlatList
          data={data}
          renderItem={renderItem}
          keyExtractor={item => item.id}
          horizontal={true}
          ListHeaderComponent={<Text style={styles.header}>《唐诗 李白》</Text>}
          ListFooterComponent={<Text style={styles.footer}>没有更多了...</Text>}
        />
      </ScrollView>
    </SafeAreaView>
  );
};

添加上horizontal属性,设置成true,就会变成横向滚动了。

  • 如果要实现下拉刷新,FlatList也直接支持使用RefreshControl

  • onEndReached表示触底后,要执行什么函数。

  • onEndReachedThreshold,表示距离列表底部还有多少可见区域时触发。例如我们这里设置成0.1,这表示距离底部还剩10%的时候,就会触发要执行的函数。

    import { useState } from "react";
    import {
    Text,
    ScrollView,
    StyleSheet,
    FlatList,
    RefreshControl,
    } from "react-native";
    import { SafeAreaView } from "react-native";

    export default function App() {
    const [refreshing, setRefreshing] = useState(false);
    const onRefresh = () => {
    setRefreshing(true);

    复制代码
      // 模拟重新读取接口
      console.log("开始读取接口了");
      setTimeout(() => {
        setRefreshing(false);
      }, 2000);
    };
    
    const onEndReached = () => {
      console.log('开始加载更多了');
    };
    
    const data = [
      { id: 1, title: "静夜思" },
      { id: 2, title: "望庐山瀑布" },
      { id: 3, title: "早发白帝城" },
      { id: 4, title: "黄鹤楼送孟浩然之广陵" },
      { id: 5, title: "将进酒" },
      { id: 6, title: "行路难·其一" },
      { id: 7, title: "蜀道难" },
      { id: 8, title: "月下独酌·其一" },
      { id: 9, title: "赠汪伦" },
      { id: 10, title: "梦游天姥吟留别" },
      { id: 11, title: "宣州谢朓楼饯别校书叔云" },
      { id: 12, title: "送友人" },
      { id: 13, title: "登金陵凤凰台" },
      { id: 14, title: "清平调·其一" },
      { id: 15, title: "秋浦歌·白发三千丈" },
      { id: 16, title: "渡荆门送别" },
      { id: 17, title: "夜宿山寺" },
      { id: 18, title: "独坐敬亭山" },
      { id: 19, title: "关山月" },
      { id: 20, title: "子夜吴歌·秋歌" },
      { id: 21, title: "下终南山过斛斯山人宿置酒" },
      { id: 22, title: "月下独酌·其二" },
      { id: 23, title: "塞下曲六首·其一" },
      { id: 24, title: "玉阶怨" },
      { id: 25, title: "春夜洛城闻笛" },
      { id: 26, title: "越中览古" },
      { id: 27, title: "山中问答" },
      { id: 28, title: "清平调·其一" },
      { id: 29, title: "清平调·其二" },
      { id: 30, title: "清平调·其三" },
    ];
    
    return (
      <SafeAreaView style={styles.container}>
        <FlatList
          data={data}
          renderItem={({ item }) => (
            <Text style={styles.title}>{item.title}</Text>
          )}
          keyExtractor={(item) => item.id}
          ListHeaderComponent={<Text style={styles.header}>《唐诗 李白》</Text>}
          ListFooterComponent={<Text style={styles.footer}>没有更多了...</Text>}
          refreshControl={
            <RefreshControl
              refreshing={refreshing}
              onRefresh={onRefresh}
              tintColor={"#1f99b0"}
            />
          }
          onEndReached={onEndReached}
          onEndReachedThreshold={0.1}
        />
      </SafeAreaView>
    );

    }

    const styles = StyleSheet.create({
    container: {
    flex: 1,
    backgroundColor: "#fff",
    },
    header: {
    textAlign: "center",
    fontSize: 50,
    lineHeight: 60,
    fontWeight: "bold",
    },
    footer: {
    textAlign: "center",
    fontSize: 30,
    lineHeight: 60,
    color: "#999",
    },
    title: {
    textAlign: "center",
    fontSize: 30,
    lineHeight: 60,
    },
    });

相关推荐
工业甲酰苯胺2 小时前
TypeScript枚举类型应用:前后端状态码映射的最简方案
javascript·typescript·状态模式
止观止2 小时前
React虚拟DOM的进化之路
前端·react.js·前端框架·reactjs·react
谢尔登2 小时前
【React Natve】NetworkError 和 TouchableOpacity 组件
前端·react.js·前端框架
G等你下课5 小时前
React 路由懒加载入门:提升首屏性能的第一步
前端·react.js·前端框架
然我6 小时前
面试官:如何判断元素是否出现过?我:三种哈希方法任你选
前端·javascript·算法
kk_stoper6 小时前
如何通过API查询实时能源期货价格
java·开发语言·javascript·数据结构·python·能源
晨枫阳6 小时前
前端VUE项目-day1
前端·javascript·vue.js
颜酱7 小时前
抽离ant-design后台的公共查询设置
前端·javascript·ant design
FogLetter7 小时前
深入浅出React-Router-Dom:从前端路由到SPA架构的华丽转身
前端·react.js