FlatList
注意:
1. 滑出渲染区域后,内部状态不保留,请确保组件以外保留了数据
2. 为了优化内存占用同时保持滑动顺畅,列表内容会在屏幕外绘制,如果滑动速度超
过渲染速度,则会先看到空白内容
3. 继承自pureComponent 而非通常的Component,意味着props在浅比较中是相等的
则不会重新渲染
4. 默认情况下每行需要提供一个不重复的key属性,也可以提供一个keyExtractor函数来生成key
1. itemSeparatorComponent:
分割线,不会出现在第一行之前和最后一行之后
2. ListEmptyComponent:
列表为空时渲染该组件,可以是React.Component 也可以是render函数/渲染好的element
3. ListFooterComponent:
尾部组件,可以实现加载更多尾部
4. ListHeaderComponent:
头部组件,当界面包含其他元素和列表的时候,可以将其他元素作为header放到
ListHeaderComponent 中
5. columnWrapperStyle:
如果设置了多布局(column > 1) ,则可以额外指定次央视作用在每个容器上
6. data:
设置列表数据,为了简化起见,data 属性目前只支持普通数组
7. extraData:
如果有除data以外的数据用在列表中(不论是用在renderItem还是头部或者尾部组件
中),请在此属性中指定。同时此数据在修改时也需要先修改其引用地址(比如先复制
到一个新的 Object 或者数组中),然后再修改其值,否则界面很可能不会刷新。
8. getItemLayout:
可选的优化,避免动态测量内容尺寸的开销,前提是提前知道内容的高度,如果行高固
定,getItemLayout用起来高效又简单
例
getItemLayout={(data, index) => (
{length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
)}
注意:
如果设置了分隔线,分隔线的尺寸也要计算到offset中
9. horizonal:
true 变为水平布局模式
10. initialNumToRender:
指定一开始渲染的元素数量,最好刚刚够填满一个屏幕,这样保证了用最短的时间给用户
呈现可见的内容。
注意:
注意这第一批次渲染的元素不会在滑动过程中被卸载,这样是为了保证
用户执行返回顶部的操作时,不需要重新渲染首批元素。
11. initialScrollIndex:
开始时屏幕顶端的元素是列表中的第 initialScrollIndex个元素, 而不是第一个元素。如果
设置了这个属性,则第一批initialNumToRender范围内的元素不会再保留在内存里,而是直
接立刻渲染位于 initialScrollIndex 位置的元素。需要先设置 getItemLayout 属性。
12. keyExtractor:
此函数用于为给定的 item 生成一个不重复的 key。Key 的作用是使 React 能够区分同类
元素的不同个体,以便在刷新时能够确定其变化的位置,减少重新渲染的开销。若不指
定此函 数,则默认抽取item.key作为 key 值。若item.key也不存在,则使用数组下标。
13. numColumn:
多列布局只能在非水平模式下使用,即必须是horizontal={false}。此时组件内元素会从左
到右从上到下按 Z 字形排列,类似启用了flexWrap的布局。组件内元素必须是等高的——
暂时还无法支持瀑布流布局。
14. onEndReached:
当列表被滚动到距离内容最底部不足onEndReachedThreshold的距离时调用。
15. onEndReachedThreshold:
决定当距离内容最底部还有多远时触发onEndReached回调。注意此参数是一个比值而
非像素单位。比如,0.5 表示距离内容最底部的距离为当前列表可见长度的一半时触发。
16. onRefresh
如果设置了此选项,则会在列表头部添加一个标准的RefreshControl控件,以便实现“下
拉刷新”的功能。同时你需要正确设置refreshing属性。
17. regreshing:
在等待加载新数据时将此属性设为 true,列表就会显示出一个正在加载的符号。
18. onViewableItemsChanged:
在可见行元素变化时调用。可见范围和变化频率等参数的配置请设置viewabilityConfig属
性。
19. renderItem:
从data中挨个取出数据并渲染到列表中。
item (Object): The item from data being rendered.
index (number): The index corresponding to this item in the data array.
separators (Object)
highlight (Function)
unhighlight (Function)
updateProps (Function)
select (enum('leading', 'trailing'))
newProps (Object)
示例,包含下拉刷新和上拉加载更多
import React, {Component} from 'react';
import {Text, View, StyleSheet, FlatList, Image, StatusBar} from 'react-native';
import AsyncStorage from '@react-native-community/async-storage';
import ToastUtil from '../base/ToastUtil';
export default class TrainNavigation extends Component {
state = {
barStyle: 'dark-content',
trainList: [],
currentPage: 1,
isRefresh: false,
isLoadMore: false,
isHasNext: true,
showFooter: 0,
};
static navigationOptions = {
tabBarLabel: '训练',
tabBarIcon: ({focused}) => {
if (focused) {
return (<Image style={styles.tabbaricon} source={require('../assets/train_selected.png')}/>
);
} else {
return (<Image style={styles.tabbaricon} source={require('../assets/train.png')}/>
);
}
},
};
componentDidMount() {
this._navListener = this.props.navigation.addListener('didFocus', () => {
StatusBar.setBarStyle('dark-content');
StatusBar.setBackgroundColor('white');
this.getTrainData(this.state.currentPage);
});
}
componentWillUnmount() {
this._navListener.remove();
}
renderFooterComponent() {
const showFooter = this.state.showFooter;
if (showFooter === 0) {
return (
<View>
</View>
);
} else if (showFooter === 1) {
return (
<View style={{alignItems: 'center', justifyContent: 'center', marginTop: 10, marginBottom: 10}}>
<Text style={{color: 'rgba(186, 191, 194, 1)', fontSize: 12}}>加载更多数据</Text>
</View>
);
} else {
return (
<View style={{alignItems: 'center', justifyContent: 'center', marginTop: 10, marginBottom: 10}}>
<Text style={{color: 'rgba(186, 191, 194, 1)', fontSize: 12}}>- 没有更多了,下周再来吧 -</Text>
</View>
);
}
}
onRefresh() {
if (!this.state.isRefresh) {
let currentPage = 1;
this.setState({currentPage: currentPage, isRefresh: true});
this.getTrainData(currentPage);
}
}
onLoadMore() {
if (!this.state.isLoadMore && this.state.trainList.length > 0 && this.state.isHasNext) {
let currentPage = this.state.currentPage;
currentPage++;
this.getTrainData(currentPage);
this.setState({
currentPage: currentPage,
isLoadMore: true,
});
}
}
async getTrainData(currentPage) {
try {
let token = await AsyncStorage.getItem(appConfig.USER_TOKEN);
let response = await fetch(httpurl.BASE_URL + url.url_train_list, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': token,
},
body: JSON.stringify({'pageNum': currentPage}),
});
const newVar = await response.json();
switch (newVar.code) {
case 200:
let {trainList, showFooter} = this.state;
if (currentPage == 1) {
if (null !== trainList && trainList.length > 0) {
trainList = [];
}
}
const busPlans = newVar.data.busPlans;
trainList = [...trainList, ...busPlans];
if (null !== trainList && trainList.length > 0) {
if (newVar.data.hasNextPage) {
showFooter = 1;
} else {
showFooter = 2;
}
} else {
showFooter = 0;
}
this.setState({
trainList: trainList,
isHasNext: newVar.data.hasNextPage,
isLoadMore: false,
isRefresh: false,
showFooter: showFooter,
});
break;
case 401:
break;
default:
ToastUtil.show(newVar.message);
currentPage--;
this.setState({
isLoadMore: false,
isRefresh: false,
currentPage: currentPage,
});
break;
}
} catch (e) {
currentPage--;
this.setState({
isLoadMore: false,
isRefresh: false,
currentPage: currentPage,
});
}
}
test() {
var obj = {
name: '123',
age: 12,
};
let array = [
{
name: ' 2342',
},
{
name: '12312',
},
];
const array1 = array.map(item => {
return {
item,
checked: false,
};
});
const selectedList = array1.filter(item => item.checked);
}
// item布局
renderItem(item, index) {
return (
<View style={styles.itemViewStyle}>
<View style={{
width: 343, height: 150, marginTop: 10,
marginBottom: 10,
}}>
<Image style={styles.itemImageStyle}
hide={true}
source={{uri: item.photo}}/>
<View style={{
width: '100%',
height: '100%',
position: 'absolute',
borderRadius: 5,
backgroundColor: item.state === 0 ? 'rgba(0,0,0,0.25)' : 'rgba(0, 0, 0, 0.6)',
justifyContent: 'flex-end',
}}>
{item.state === 0 ? <Text
style={{
fontSize: 22,
color: 'white',
fontWeight: 'bold',
marginLeft: 12,
}}>{item.title} </Text> :
<View style={{flexDirection: 'row'}}>
<Image style={{width: 30, height: 30, marginLeft: 12}}
source={require('../assets/ic_clock.png')}/>
<Text style={{
fontSize: 22,
color: 'white',
fontWeight: 'bold',
}}>{item.title} </Text></View>}
{/*"Day" + (position + 1) + " · " + bean.getMinute() + "分钟"*/}
<Text style={{
fontSize: 12,
color: 'rgba(222, 222, 222, 1)',
fontWeight: 'bold',
marginLeft: 12,
marginBottom: 9,
}}>{'Day' + index + 1 + ' . ' + item.minute + '分钟'} </Text>
</View>
</View>
</View>
);
}
renderEmptyComponent() {
return (
<View style={{
width: '100%',
height: '100%',
backgroundColor: 'white',
justifyContent: 'center',
alignItems: 'center',
}}>
<Image style={{width: 343, height: 238, marginTop: 143}}
source={require('../assets/ic_train_empty.png')}/>
<Text style={{color: 'rgba(99, 109, 118, 1)', fontSize: 18}}>活动暂未开启,快来报名参加吧!</Text>
</View>
);
}
render() {
const {trainList, isRefresh} = this.state;
return (
<View style={{height: '100%', backgroundColor: 'rgba(255, 255, 255, 1)'}}>
<StatusBar
barStyle={'dark-content'}
hidden={false}
translucent={false}
backgroundColor={'white'}/>
<View style={styles.headerView}>
<Text style={styles.titleText}>训练计划</Text>
</View>
<Text style={[styles.titleText, {
fontSize: 13,
marginLeft: 18,
marginBottom: 3,
}]}>{trainList.length > 0 ? '请您在保障自身安全情况下,根据自身情况完成相应计划。' : ''}</Text>
<FlatList
data={trainList}
renderItem={({item, index}) => this.renderItem(item, index)}
keyExtractor={(item, index) => index.toString()}
onRefresh={() => this.onRefresh()}
refreshing={isRefresh}
onEndReached={() => this.onLoadMore()}
onEndReachedThreshold={0.1}
ListFooterComponent={() => this.renderFooterComponent()}
ListEmptyComponent={() => this.renderEmptyComponent()}
/>
</View>
);
}
}
const styles = StyleSheet.create({
headerView: {
width: '100%',
height: 44,
backgroundColor: 'white',
flexDirection: 'row',
alignItems: 'center',
},
tabbaricon: {
width: 24,
height: 24,
},
titleText: {
marginLeft: 16,
fontSize: 18,
color: 'rgba(99, 109, 118, 1)',
},
itemViewStyle: {
position: 'relative',
justifyContent: 'flex-end',
alignItems: 'center',
},
itemImageStyle: {
width: 343,
height: 150,
borderRadius: 5,
resizeMode: 'cover',
},
});