1.封裝 Scroller 組件
src/components/Scroller/index.js
/**
* 下拉刷新/上拉加載更多 組件(Scroller)
*/
import React, {Component} from 'react';
import {
StyleSheet,
Text,
View,
ListView,
ActivityIndicator,
RefreshControl,
} from 'react-native';
export default class Scroller extends Component {
// 構造函數
constructor(props) {
super(props);
this.state = {
//
}
}
render() {
const { dataSource, renderRow, isRefreshing } = this.props;
// console.log(this.props);
return (
<View style={styles.container}>
{/*列表數據*/}
<ListView
// 數據源
dataSource={dataSource}
// 從數據源(dataSource)中接受一條數據,以及它和它所在section的ID
renderRow={renderRow}
// 頁頭與頁腳會在每次渲染過程中都重新渲染(允許在ListView底部增加一欄,便於顯示加載動畫)
renderFooter={this._renderFooter.bind(this)}
// 當所有的數據都已經渲染過,並且列表被滾動到距離最底部不足onEndReachedThreshold個像素的距離時調用
onEndReached={this._fetchMoreData.bind(this)}
// 調用onEndReached之前的臨界值,單位是像素。(預加載)
onEndReachedThreshold={20}
// 隱藏右側滾動條
showsVerticalScrollIndicator={false}
// finished warning : in next release ...
enableEmptySections={true}
// 自動調整遷移內容
// 導航欄或標簽欄或工具欄不覆蓋 Scrollview 內容
// 去除默認定位間距
automaticallyAdjustContentInsets={false}
// 下拉刷新
refreshControl={
<RefreshControl
// 是否刷新
refreshing={isRefreshing}
onRefresh={this._onRefresh.bind(this)}
tintColor={"#ff6600"}
title={"拼命加載中..."}
/>
}
/>
</View>
)
}
/**
* 下拉刷新
*/
_onRefresh() {
// console.log('下拉刷新');
if (this.props.isRefreshing || !this._hasMore()) {
return
}
// 向后台發送 '0',告知刷新操作
this.props.fetchData(0);
}
/**
* 加 _ 代表私有方法
* 上拉加載更多
*/
_fetchMoreData() {
// console.log('上拉加載更多');
/**
* this._hasMore() 驗證還有更多數據
* isLoadingTail true/false 加載動畫(菊花圖)
*/
if (!this._hasMore() || this.props.isLoadingTail) {
return
}
let page = this.props.cachedResults.nextPage;
this.props.fetchData(page);
}
/**
* 驗證還有更多數據
*/
_hasMore() {
return this.props.cachedResults.items.length !== this.props.cachedResults.items.total;
}
/**
* 底部加載動畫 及 沒有更多數據文本(ListView底部增加一欄,便於顯示加載動畫)
*/
_renderFooter() {
if (!this._hasMore() && this.props.cachedResults.total !== 0) {
return (
<View style={styles.loadingMore}>
<Text style={styles.loadingText}>沒有更多了</Text>
</View>
)
}
if (!this.props.isLoadingTail) {
return (
<View style={styles.loadingMore}></View>
)
}
// 菊花圖
return (
<ActivityIndicator style={styles.loadingMore}/>
)
}
}
// 樣式
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
// 菊花圖
loadingMore: {
marginVertical: 20
},
// 文案樣式
loadingText: {
color: '#777',
textAlign: 'center'
}
});
2.頁面調用
/**
* 視頻列表頁
*/
import React, {Component} from 'react';
import {
StyleSheet,
Text,
View,
ImageBackground,
ListView,
TouchableHighlight,
Alert,
Dimensions,
ActivityIndicator,
RefreshControl,
} from 'react-native';
// 下拉刷新/上拉加載更多組件
import Scroller from '../../components/Scroller';
// 圖標
import Icon from 'react-native-vector-icons/Ionicons';
// item 組件
import CreationItem from '../../components/CreationItem';
import config from '../../common/config';
import request from '../../common/request';
let {width} = Dimensions.get("window");
// 緩存列表中所有數據
let cachedResults = {
nextPage: 1, // 下一頁
items: [], // listview 數據(視頻列表)
total: 0 // 總數
};
export default class List extends Component {
// 構造函數
constructor() {
super();
let ds = new ListView.DataSource({
// 比較兩條數據是否是一樣的,來判斷數據是否發生改變
rowHasChanged: (r1, r2) => r1 !== r2
});
this.state = {
dataSource: ds.cloneWithRows([]),
isLoadingTail: false, // loading?
isRefreshing: false // refresh?
}
}
render() {
return (
<View style={styles.container}>
{/*頂部標題欄*/}
<View style={styles.header}>
<Text style={styles.headerTitle}>列表頁面</Text>
</View>
{/*列表數據*/}
<Scroller
// 數據源
dataSource={this.state.dataSource}
// 渲染item(子組件)
renderRow={this._renderRow.bind(this)}
// 是否可以刷新
isRefreshing={this.state.isRefreshing}
// 是否可以加載更多
isLoadingTail={this.state.isLoadingTail}
// 請求數據
fetchData={this._fetchData.bind(this)}
// 緩存列表數據
cachedResults={cachedResults}
/>
</View>
)
}
// 生命周期-組件掛載完畢 請求數據
componentDidMount() {
this._fetchData(1);
}
// 請求數據
_fetchData(page) {
let that = this;
if (page !== 0) { // 加載更多操作
this.setState({
isLoadingTail: true
});
} else { // 刷新操作
this.setState({
isRefreshing: true
});
// 初始哈 nextPage
cachedResults.nextPage = 1;
}
request
.get(config.api.base + config.api.creations, {
accessToken: 'abc'
})
// data 變化的新數據
.then((data) => {
if (data.success) {
// 保存原數據
let items = cachedResults.items.slice();
if (page !== 0) { // 加載更多操作
// 數組拼接
items = items.concat(data.data);
cachedResults.nextPage += 1;
} else { // 刷新操作
// 數據不變
items = data.data;
}
cachedResults.items = items; // 視頻列表數據
cachedResults.total = data.total; // 總數
setTimeout(function () {
if (page !== 0) { // 加載更多操作
that.setState({
isLoadingTail: false,
dataSource: that.state.dataSource.cloneWithRows(cachedResults.items)
});
} else { // 刷次操作
that.setState({
isRefreshing: false,
dataSource: that.state.dataSource.cloneWithRows(cachedResults.items)
});
}
}, 1000);
}
})
.catch((error) => {
if (page !== 0) { // 上拉加載更多操作
this.setState({
isLoadingTail: false
});
} else {
this.setState({ // 刷新操作
isRefreshing: false
});
}
console.error(error);
});
}
// 列表 Item
_renderRow(row) {
const { navigation } = this.props;
return (
<CreationItem
navigation={navigation}
key={row.id} // 子組件唯一性
row={row}
/>
)
}
}
// 樣式
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
// 頭部樣式
header: {
paddingTop: 25,
paddingBottom: 12,
backgroundColor: '#ee735c',
},
// 頭部title樣式
headerTitle: {
color: '#fff',
fontSize: 16,
textAlign: 'center',
fontWeight: '600'
},
// 菊花圖
loadingMore: {
marginVertical: 20
},
// 文案樣式
loadingText: {
color: '#777',
textAlign: 'center'
}
});
3.效果圖
