React Native系统组件三
1. FlatList 的使用与分析
一、功能简介
FlatList高性能的简单列表组件,支持下面这些常用的功能:
- 完全跨平台。
- 支持水平布局模式。
- 行组件显示或隐藏时可配置回调事件。
- 支持单独的头部组件。
- 支持单独的尾部组件。
- 支持自定义行间分隔线。
- 支持下拉刷新。
- 支持上拉加载。
- 支持跳转到指定行(ScrollToIndex)。
二、属性说明
FlatList
有三个核心属性 data
renderItem
getItemLayout
。它继承自 ScrollView
组件,所以拥有ScrollView
的属性和方法。
data
和 ListView 不同,它没有特殊的 DataSource 数据类型作为传入参数。它接收的仅仅只是一个 Array<object> 作为参数。
参数数组中的每一项,需要包含 key 值作为唯一标示。数据结构如下:
[
{
infoId: "1030296374794543105",
price: "31万",
warnType: 0,
spdz: "",
postDate: "08-17",
state: 2,
syncResult: "已同步2 失败0 同步中0",
pic: "/p1/tiny/n_v2862e29d1f2854bbebf208d0a520591a9.jpg",
title: "丰田 普拉多 2010款 4.0L 自动TX",
age: "库龄35天",
intention: 0
}
]
renderItem
它接收一个函数作为参数,该函数返回一个 ReactElement
。函数的第一个参数的 item
是data
属性中的每个列表的数据( Array<object>
中的 object
) 。这样就将列表元素和数据结合在一起,生成了列表,示例:
_renderItem({ item, index }) {
let { infoId, title, pic, price, syncResult, postDate, age, state } = item;
let url = "https://pic2.58cdn.com.cn" + pic;
let icon = {
2: require("./../img/yishou.png"),
0: require("./../img/tuiku.png")
};
return (
<TouchableHighlight style={styles.carList}>
<View style={styles.carView}>
{state == 1 ? null :
<Image style={styles.carIcon} source={icon[state]} />}
<Image style={styles.carImg} source={{ uri: url }} />
<View style={styles.carInfo}>
<Text numberOfLines={1} style={styles.carTitle}>
{title}
</Text>
<View style={styles.carTime}>
<Text style={styles.carDate}>{postDate}</Text>
<Text style={styles.carAge}>{age}</Text>
</View>
<View style={styles.carState}>
<Text style={styles.carPrice}>{price}</Text>
<Text style={styles.carStatus}>{syncResult}</Text>
</View>
</View>
</View>
</TouchableHighlight>
);
}
getItemLayout
可选优化项。但是实际测试中,如果不做该项优化,性能会差很多。所以强烈建议做此项优化!
如果不做该项优化,每个列表都需要事先渲染一次,动态地取得其渲染尺寸,然后再真正地渲染到页面中。
如果预先知道列表中的每一项的高度(ITEM_HEIGHT)和其在父组件中的偏移量(offset)和位置(index),就能减少一次渲染。这是很关键的性能优化点
getItemLayout={(data, index) =>
// 90 是被渲染 item 的高度 ITEM_HEIGHT。
({ length: 90, offset: 90 * index, index })
}
extraData
如果有除data以外的数据用在列表中(不论是用在renderItem
还是Header或者Footer中),请在此属性中指定。同时此数据在修改时也需要先修改其引用地址(比如先复制到一个新的Object或者数组中),然后再修改其值,否则界面很可能不会刷新。(非常重要)
//确保state更新时列表及时更新
extraData={this.state}
ListEmptyComponent
列表为空时渲染该组件。可以是React Component, 也可以是一个render
函数, 或者渲染好的element。
_renderEmptyView = () => {
return (
<View style={styles.listEmpty}>
<Image style={styles.listEmptyImg}
source={require("./../img/norecord.png")} />
<Text style={styles.listEmptyText}>您暂无符合此筛选条件的车源</Text>
</View>
);
};
keyExtractor
此函数用于为给定的item
生成一个不重复的key
。Key
的作用是使React
能够区分同类元素的不同个体,以便在刷新时能够确定其变化的位置,减少重新渲染的开销。若不指定此函数,则默认抽取item.key
作为key
值。若item.key
也不存在,则使用数组下标。
keyExtractor={(item, index) => item.infoId}
下拉刷新、上拉加载
onRefresh={() => this._onRefresh()}
refreshing={this.state.isRefresh}
//加载更多
onEndReached={() => this._onLoadMore()}
//当列表被滚动到距离内容最底部不足onEndReachedThreshold的距离时调用
onEndReachedThreshold={0.1}
refreshing
在等待加载新数据时将此属性设为true,列表就会显示出一个正在加载的符号。onRefresh
如果设置了此选项,则会在列表头部添加一个标准的RefreshControl
控件,以便实现“下拉刷新”的功能。同时你需要正确设置refreshing
属性。onEndReachedThreshold
决定当距离内容最底部还有多远时触发onEndReached
回调。注意此参数是一个比值而非像素单位。比如,0.5表示距离内容最底部的距离为当前列表可见长度的一半时触发。onEndReached
当列表被滚动到距离内容最底部不足onEndReachedThreshold
的距离时调用。
constructor(props) {
super(props);
this.page = 1;
this.state = {
// 下拉刷新
isRefresh: false,
// 加载更多
isLoadMore: false
};
}
_onRefresh() {
// 不处于 下拉刷新
if (!this.state.isRefresh) {
this.page = 1;
let request = {
cateId: 29,
pageNum: this.page,
pageSize: 20
};
request = this.mergeJson(request, this.state.filterVaule);
this.getSourceList(request);
}
}
getSourceList(request) {
WBCST.getFetch("https://***.com/ershouche/stock/getList", request).then(
(response) => {
if (response && response.respCode == 0 && response.respData) {
if (this.page == 1) {
//重新渲染
this.setState({
data: response.respData
});
} else {
let loadMore = false;
if (response.respData.length < request.pageSize) {
//显示到底部了 没有更多数据了
loadMore = true;
}
this.setState({
//加载更多 这个变量不刷新
isLoadMore: loadMore,
//添加新数据源
data: this.state.data.concat(response.respData)
});
}
} else {
// WBCST.loading({ state: "dismiss" });
this.setState({
data: stockList.respData
// data: []
});
Toast.toast({ text: "当前网络不可用,请检查网络设置!", state: "error" });
}
}
);
}
2. SectionList
- 基础使用:sections、renderItem、keyExtractor
- ScrollView属性:内容容器、滚动条、滚动监听、键盘模式等
- 表头:ListHeaderComponent
- 表尾:ListFooterComponent
- 分组头部:renderSectionHeader
- 分割线元素:ItemSeparatorComponent
- 分组吸顶:stickySectionHeadersEnabled
- 滚动api:scrollToLocation()
import { View, StyleSheet, SectionList, Text } from "react-native"
import { SectionData } from "../constants/Data";
import { useEffect, useRef } from "react";
export default () => {
const renderItem = ({item, index, section}) => {
return (
<Text style={styles.txt}>{item}</Text>
);
};
const renderSectionHeader = ({section}) => {
return (
<Text style={styles.sectionHeader}>{section.type}</Text>
);
};
const sectionListRef = useRef(null);
useEffect(() => {
setTimeout(() => {
sectionListRef.current.scrollToLocation({
sectionIndex: 1,
itemIndex: 2,
// viewPosition: 0,
});
}, 2000);
}, []);
return (
<SectionList
ref={sectionListRef}
style={styles.root}
sections={SectionData}
renderItem={renderItem}
keyExtractor={(item, index) => `item-${index}`}
renderSectionHeader={renderSectionHeader}
stickySectionHeadersEnabled={true}
>
</SectionList>
);
}
const styles = StyleSheet.create({
root: {
width: '100%',
height: '100%',
backgroundColor: '#f0f0f0',
},
txt: {
fontSize: 20,
height: 56,
width: '100%',
color: "#333",
textAlignVertical: 'center',
textAlign: 'center'
},
sectionHeader: {
width: '100%',
height: 36,
backgroundColor: 'red',
textAlign: 'center',
fontSize: 22,
}
});
3.Model
- 控制显示:visible
- 渲染内容:children
- 安卓返回关闭:onRequestClose
- 背景透明:transparent
- 状态栏透明:statusBarTranslucent
- 动画方式:animationType
- 状态回调:onShow、onDismiss
- 背景动画:伏笔
import { View, StyleSheet, Modal, Text, SectionList, Button, Image, TouchableOpacity } from "react-native"
import { SectionData } from "../constants/Data";
import { useRef, useState } from "react";
import close from "../assets/images/icon_close_modal.png"
export default () => {
const [visible, setVisible] = useState(false);
const showModal = () => {
setVisible(true);
}
const hideModal = () => {
setVisible(false);
}
const renderItem = ({ item, index, section }) => {
return (
<Text style={styles.txt}>{item}</Text>
);
};
const renderSectionHeader = ({ section }) => {
return (
<View style={styles.sectionHeader}>
<Text style={styles.sectionHeader}>{section.type}</Text>
</View>
);
};
const listHeader = () => {
return (
<View style={styles.listHeader}>
<Text style={styles.listHeader}>
列表头部
</Text>
<TouchableOpacity
style={styles.iconButton}
onPress={() => {
hideModal()
}}
>
<Image
source={close}
style={styles.icon}
/>
</TouchableOpacity>
</View>
);
}
return (
<View style={styles.root}>
<Button title="按钮" onPress={() => {
showModal();
}} />
<Modal
visible={visible}
onRequestClose={() => {
hideModal();
}}
transparent={true}
// statusBarTranslucent={true}
animationType="slide"
onShow={() => {
console.log('onShow...')
}}
onDismiss={() => {
//有问题,不回调
console.log('onDismiss...')
}}
>
<View style={styles.blank}></View>
<View style={styles.content}>
<SectionList
style={styles.sectionList}
sections={SectionData}
renderItem={renderItem}
keyExtractor={(item, index) => `item-${index}`}
renderSectionHeader={renderSectionHeader}
stickySectionHeadersEnabled={true}
ListHeaderComponent={listHeader}
>
</SectionList>
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
root: {
width: '100%',
height: '100%',
padding: 20,
},
content: {
width: '100%',
height: '90%',
backgroundColor: "#ff000030"
},
sectionList: {
width: '100%',
height: '100%',
backgroundColor: '#f0f0f0',
},
txt: {
fontSize: 20,
height: 56,
width: '100%',
color: "#333",
textAlignVertical: 'center',
textAlign: 'center'
},
sectionHeader: {
width: '100%',
height: 36,
backgroundColor: 'red',
textAlign: 'center',
fontSize: 22,
justifyContent: 'center'
},
blank: {
width: '100%',
height: '10%',
backgroundColor: '#00000050'
},
icon: {
width: 24,
height: 24,
},
iconButton: {
// width: 24,
// height: 24,
position: 'absolute',
right: 16
},
listHeader: {
width: '100%',
height: 36,
backgroundColor: 'yellow',
textAlign: 'center',
fontSize: 22,
justifyContent: 'center',
textAlignVertical: 'center'
},
})
4.StatusBar:状态栏适配的难题交我
- 内容深浅模式:barStyle
- 背景颜色:backgroundColor
- 动画切换:animated
- 透明悬浮:translucent
- 隐藏状态栏:hidden
- api:setBackgroundColor()、setBarStyle()、setHidden()、setTranslucent()
<StatusBar
barStyle={"dark-content"}
backgroundColor='white'
// animated={true}
// translucent={true}
hidden={false}
/>