一、无图无真相,放上效果图先
二、封装的思路可参考之前的文章:https://blog.csdn.net/d06110902002/article/details/77929490
三、数据模型基类:
/**
* FlatList 的数据模型基类
* 对于基要展示的所有数据均扩展此类
*/
import ModelType from './ModelType';
export default class BaseModel{
/**
* 子类需要扩展本接口
* @returns {string}
*/
getItemType(){
return ModelType.default;
};
}
任何要显示在flatList 中的Item,都需要定义一个数据model,然后扩展上面那个基类,最主要的是重载getItemType接口以示区分这是哪个item对应的model,本例子主要有2种item,它们对应的model分别如下:
/**
* 标题文本
*/
import BaseModel from './BaseModel'
import ModelType from "./ModelType";
export default class TitleModel extends BaseModel{
constructor(){
super();
this.titleContent = '';
};
getItemType(){
return ModelType.Title;
};
}
/**
* 大学数据模型
*/
import BaseModel from './BaseModel'
import ModelType from "./ModelType";
export default class UniversityModel extends BaseModel{
constructor(){
super();
this.universityName = '';
this.place = '';
};
getItemType(){
return ModelType.University;
};
}
区分数据模型的枚举代码如下:
/**
* 数据模型的类型
*/
export default {
default: 'default',
Title: 'Title',
University:'University',
}
三、FlatList 接口的相关封装:
3.1 底部视图:
import React, {Component} from 'react';
import {View, Text, ActivityIndicator, StyleSheet, TouchableOpacity} from 'react-native';
import RefreshState from './RefreshState';
import PropTypes from 'prop-types';
export default class RefreshFooter extends Component {
static propTypes = {
onLoadMore: PropTypes.func, // 加载更多数据的方法
onRetryLoading: PropTypes.func, // 重新加载的方法
};
static defaultProps = {
footerRefreshingText: "努力加载中",
footerLoadMoreText: "上拉加载更多",
footerFailureText: "点击重新加载",
footerNoMoreDataText: "已全部加载完毕"
};
render() {
let {state} = this.props;
let footer = null;
switch (state) {
case RefreshState.Idle:
// Idle情况下为null,不显示尾部组件
break;
case RefreshState.Refreshing:
footer =
<View style={styles.loadingView}>
<ActivityIndicator size="small"/>
<Text style={styles.refreshingText}>{this.props.footerRefreshingText}</Text>
</View>;
break;
case RefreshState.CanLoadMore:
footer =
<View style={styles.loadingView}>
<Text style={styles.footerText}>{this.props.footerLoadMoreText}</Text>
</View>;
break;
case RefreshState.NoMoreData:
footer =
<View style={styles.loadingView}>
<Text style={styles.footerText}>{this.props.footerNoMoreDataText}</Text>
</View>;
break;
case RefreshState.Failure:
footer =
<TouchableOpacity style={styles.loadingView} onPress={()=>{
this.props.onRetryLoading && this.props.onRetryLoading();
}}>
<Text style={styles.footerText}>{this.props.footerFailureText}</Text>
</TouchableOpacity>;
break;
}
return footer;
}
}
const styles = StyleSheet.create({
loadingView: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
padding: 15,
},
refreshingText: {
fontSize: 12,
color: "#666666",
paddingLeft: 10,
},
footerText: {
fontSize: 12,
color: "#666666"
}
});
flatList 的封装,其中上拉加载更多,与下拉刷新接口通过属性的方式传递:
import React, {Component} from 'react';
import {FlatList} from 'react-native';
import PropTypes from 'prop-types';
import RefreshState from './RefreshState';
import RefreshFooter from './RefreshFooter';
export default class RefreshListView extends Component {
static propTypes = {
onHeaderRefresh: PropTypes.func, // 下拉刷新的方法
onFooterRefresh: PropTypes.func, // 上拉加载的方法
};
constructor(props) {
super(props);
this.state = {
isHeaderRefreshing: false, // 头部是否正在刷新
isFooterRefreshing: false, // 尾部是否正在刷新
footerState: RefreshState.Idle, // 尾部当前的状态,默认为Idle,不显示控件
}
}
render() {
return (
<FlatList
{...this.props}
onRefresh={()=>{ this.beginHeaderRefresh() }}
keyExtractor={(item,index) => index +" "} //防止出现属性Each child in an array iterator should have a unique "key" prop
refreshing={this.state.isHeaderRefreshing}
onEndReached={() => { this.beginFooterRefresh() }}
onEndReachedThreshold={0.1} // 这里取值0.1,可以根据实际情况调整,取值尽量小
ListFooterComponent={this._renderFooter}
/>
)
}
_renderFooter = () => {
return (
<RefreshFooter
state={this.state.footerState}
onRetryLoading={()=>{
this.beginFooterRefresh()
}}
/>
)
};
/// 尾部组件的状态,供外部调用,一般不会用到
footerState() {
return this.state.footerState;
}
/// 开始下拉刷新
beginHeaderRefresh() {
if (this.shouldStartHeaderRefreshing()) {
this.startHeaderRefreshing();
}
}
/// 开始上拉加载更多
beginFooterRefresh() {
if (this.shouldStartFooterRefreshing()) {
this.startFooterRefreshing();
}
}
/// 下拉刷新,设置完刷新状态后再调用刷新方法,使页面上可以显示出加载中的UI,注意这里setState写法
startHeaderRefreshing() {
this.setState(
{
isHeaderRefreshing: true
},
() => {
this.props.onHeaderRefresh && this.props.onHeaderRefresh();
}
);
}
/// 上拉加载更多,将底部刷新状态改为正在刷新,然后调用刷新方法,页面上可以显示出加载中的UI,注意这里setState写法
startFooterRefreshing() {
this.setState(
{
footerState: RefreshState.Refreshing,
isFooterRefreshing: true
},
() => {
this.props.onFooterRefresh && this.props.onFooterRefresh();
}
);
}
/***
* 当前是否可以进行下拉刷新
* @returns {boolean}
*
* 如果列表尾部正在执行上拉加载,就返回false
* 如果列表头部已经在刷新中了,就返回false
*/
shouldStartHeaderRefreshing() {
if (this.state.footerState === RefreshState.refreshing ||
this.state.isHeaderRefreshing ||
this.state.isFooterRefreshing) {
return false;
}
return true;
}
/***
* 当前是否可以进行上拉加载更多
* @returns {boolean}
*
* 如果底部已经在刷新,返回false
* 如果底部状态是没有更多数据了,返回false
* 如果头部在刷新,则返回false
* 如果列表数据为空,则返回false(初始状态下列表是空的,这时候肯定不需要上拉加载更多,而应该执行下拉刷新)
*/
shouldStartFooterRefreshing() {
if (this.state.footerState === RefreshState.refreshing ||
this.state.footerState === RefreshState.NoMoreData ||
this.props.data.length === 0 ||
this.state.isHeaderRefreshing ||
this.state.isFooterRefreshing) {
return false;
}
return true;
}
/**
* 根据尾部组件状态来停止刷新
* @param footerState
*
* 如果刷新完成,当前列表数据源是空的,就不显示尾部组件了。
* 这里这样做是因为通常列表无数据时,我们会显示一个空白页,如果再显示尾部组件如"没有更多数据了"就显得很多余
*/
endRefreshing(footerState: RefreshState) {
let footerRefreshState = footerState;
if (this.props.data.length === 0) {
footerRefreshState = RefreshState.Idle;
}
this.setState({
footerState: footerRefreshState,
isHeaderRefreshing: false,
isFooterRefreshing: false
})
}
}
四、调用方式
import React, {Component, PropTypes} from 'react';
import {
StyleSheet,
Text,
ListView,
Image,
TouchableOpacity,
RefreshControl,
View,
Alert,
Button,
FlatList,
ActivityIndicator
} from 'react-native';
import RefreshListView from './Refresh/RefreshListView';
import RefreshState from './Refresh/RefreshState';
import ItemViewMgr from "./Refresh/view/ItemViewMgr";
import TitleModel from "./Refresh/model/TitleModel";
import ModelType from "./Refresh/model/ModelType";
import BaseModel from "./Refresh/model/BaseModel";
import UniversityModel from "./Refresh/model/UniversityModel";
var testArray = [];
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
export default class LXBMultiTypeFlatList extends Component<Props> {
constructor(props) {
super(props);
this.state = {
isLoading: true,
noMoreData:false,
dataArray: this.initData(),
}
}
initData(){
for(let i = 0 ; i < 5; i++){
if( i % 2 === 0){
var title = new TitleModel();
title.titleContent = 'title_'+i;
testArray.push(title);
}else{
var university = new UniversityModel();
university.universityName = '清华大学';
university.place = '北京';
testArray.push(university);
}
}
return testArray;
};
componentDidMount() {
this.listView.beginHeaderRefresh();
}
// 渲染listView
render() {
return (
<View style={styles.container}>
<RefreshListView
ref={(ref) => {this.listView = ref}}
data={this.state.dataArray}
renderItem={this._renderItem.bind(this)}
ListEmptyComponent={this._renderEmptyView}
onHeaderRefresh={() => { this.pullDownRefresh() }}
onFooterRefresh={() => { this.loadMore() }}
/>
</View>
);
}
_renderEmptyView = (item) => {
return <View/>
};
pullDownRefresh(){
this.setState({
isLoading: true,
},()=>{
setTimeout(() => {
this.listView.endRefreshing(RefreshState.Idle);
console.log('226-----------:下拉刷新');
}, 2000)
});
}
_renderItem= (data)=> {
return (
<ItemViewMgr
itemType = {data.item.getItemType()}
ItemData = {data.item} //属性传值
onItemClickListener = {(itemData) =>this.onClickListener(itemData)}
/>
)
};
onClickListener(itemData){
Alert.alert(itemData.universityName);
};
loadMore(){
setTimeout(() => {
this.setState({
noMoreData: true,
},()=>{
this.listView.endRefreshing(RefreshState.NoMoreData);
console.log("107--------loadmore");
})
}, 2000);
console.log("107--------loadmore");
};
}
const styles = StyleSheet.create({
container: {
//flex: 1,
marginTop:50,
height:400,
},
clickButtonStyle: {
margin: 15,
height: 40,
backgroundColor: 'blue',
borderRadius: 5,
borderWidth: 0.5,
borderColor: '#7FFF00',
},
item: {
backgroundColor: '#c5ecff',
height: 50,
marginRight: 15,
marginLeft: 15,
marginBottom: 15,
alignItems: 'center',
justifyContent: 'center',
elevation:5,//漂浮的效果
borderRadius:5,//圆角
},
});