React Native_手把手教你做專案(五.下拉重新整理RefreshControl&封裝自定義Cell)
阿新 • • 發佈:2019-01-23
接下來我們繼續下拉重新整理的功能,主要是快取資料的拼接與後臺伺服器的配合。把資料最後的id傳給後臺,後臺根據id返回給你新的id之後的資料,因為沒有伺服器,所以這裡的程式碼僅僅做演示使用。
下拉重新整理RefreshControl
list.js
import React, {Component} from 'react';
import {
Platform,
StyleSheet,
Text,
View,
ListView,
TouchableOpacity,
Image,
ActivityIndicator,
RefreshControl
} from 'react-native' ;
import Icon from 'react-native-vector-icons/Ionicons';
import Dimensions from 'Dimensions';
const {width, height} = Dimensions.get('window');
import config from '../common/config';
import request from '../common/request';
// Mockjs 解析隨機的文字
import Mock from 'mockjs';
let cachedResult = {
nexPage: 1 ,
item: [],
total: 0,
}
export default class list extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (r1, r2)=>r1 !== r2,
}),
isLoadingTail: false , //沒有載入資料
isRefreshing: false
}
}
//即將顯示
componentWillMount() {
//載入本地資料
// this.dsfetchData();
}
componentDidMount() {
//載入網路資料
this._fetchData(1);
}
_fetchData(page) {
if (page !== 0) {
this.setState({
isLoadingTail: true
});
} else {
this.setState({
isRefreshing: true
});
}
this.setState({
isLoadingTail: true
});
//傳送網路請求
request.get(config.api.base + config.api.list, {
accessToken: '001',
a: 'list',
c: 'data',
type: 29,
page: page
}).then(
(data) => {
//將伺服器得到的資料快取進來
let items = cachedResult.item.slice();
// let items = cachedResult.item.concat(data.list);//把快取的資料進行拼接
if (page !== 0) {//載入更多
items = items.concat(data.list);
cachedResult.nexPage += 1;
} else {//重新整理資料
items = data.list.concat(items);
}
//最後儲存資料
cachedResult.item = items;
cachedResult.total = items.total;
// console.log(items);
// console.log('總資料是:' + cachedResult.total);
// console.log('當前到了第:' + cachedResult.item.length + '個!');
if (page !== 0) {//載入更多
this.setState({
dataSource: this.state.dataSource.cloneWithRows(
cachedResult.item
),
isLoadingTail: false
});
}else {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(
cachedResult.item
),
isRefreshing: false
});
}
}
).catch(//如果有錯
(error) => {
if(page !=0){
this.setState({
isLoadingTail:false
})
}else {
this.setState({
isRefreshing:false
})
}
console.log('err' + error);
}
)
}
// dsfetchData() {
// let result = Mock.mock({
// "data|20": [
// {
// "_id": "@ID",
// "thumb": "@IMG(1024x700,@COLOR(),\'\u56fe\u7247\u4e0a\u7684\u6587\u5b57\')",
// "title": "@cparagraph(1, 3)",
// "video": "\'http:\/\/v.youku.com\/v_show\/id_XMzY5ODY5MDI3Ng==.html?spm=a2h1n.8251846.0.0\'"
// }
// ],
// "success": true
// })
// this.setState({
// dataSource: this.state.dataSource.cloneWithRows(result.data)
// })
// }
render() {
return (
<View style={styles.container}>
{/*導航條*/}
<View style={styles.header}>
<Text style={styles.headerText}>
視訊列表
</Text>
</View>
{/*列表頁面*/}
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
style={styles.listView}
onEndReached={this._fetchMoreData}//滾到底部載入更多資料
onEndReachedThreshold={20} //距離底部還有多遠觸發
renderFooter={this._renderFooter}
refreshControl={
<RefreshControl
refreshing={this.state.isRefreshing}
onRefresh={this._onRefresh}
/>
}
/>
</View>
);
}
_onRefresh = ()=> {
if (!this._hasMore() || this.state.isRefreshing) {
return
}
this._fetchData(0);
}
//自定義Footer檢視
_renderFooter = ()=> {
if (!this._hasMore() && cachedResult.total !== 0) {
return (
<View style={styles.loadingMore}>
<Text style={styles.loadingText}>沒有更多資料啦...</Text>
</View>
)
}
// if(!this.state.isLoadingTail){
// return(<View></View>)
// }
//顯示一朵小菊花
return (
<ActivityIndicator style={styles.loadingMore}></ActivityIndicator>
)
}
//思路:有多種解決方案
//1.傳送請求 2.儲存請求引數 3.對比引數
//重新整理請求 2.儲存request = refresh 3.對比 refresh==儲存request 狀態機中的page值
//狀態機 loading = refresh
//上拉載入更多資料
_fetchMoreData = ()=> {
//判斷是否有更多資料或者是否在請求中
if (!this._hasMore || this.isLoadingTail) {
return
}
let page = cachedResult.nexPage;
//去伺服器去載入更多資料
this._fetchData(page);
}
//判斷是否還有更多資料
_hasMore() {
return cachedResult.item.length !== cachedResult.total;
}
//下劃線代表內部類自己用的函式,屬於規範
_renderRow = (rowData)=> {
return (
<TouchableOpacity>
{/*整個Cell*/}
<View style={styles.cellStyle}>
{/*標題文字*/}
<Text style={styles.title}>{rowData.text}</Text>
<Image style={styles.thumb} source={{uri: rowData.profile_image}}>
</Image>
<Icon name="ios-play"
size={30}
style={styles.play}
/>
{/*點贊&評論*/}
<View style={styles.cellFooter}>
{/*點贊*/}
<View style={styles.footerBox}>
<Icon name="ios-heart-outline"
size={30}
style={styles.boxIcon}
/>
{/*點贊文字*/}
<Text style={styles.boxText}>點贊</Text>
</View>
{/*評論*/}
<View style={styles.footerBox}>
<Icon name="ios-chatbubbles-outline"
size={30}
style={styles.boxIcon}
/>
{/*點贊文字*/}
<Text style={styles.boxText}>評論</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
header: {
// marginTop:Platform.OS == 'ios'?20:0,
paddingTop: 25,
paddingBottom: 15,
backgroundColor: '#dddddd',
borderBottomWidth: 0.5,
borderBottomColor: 'black',
},
headerText: {
fontWeight: '600',
textAlign: 'center',
fontSize: 16,
},
listView: {},
cellStyle: {
width: width,
marginTop: 10,
backgroundColor: 'white',
},
title: {
padding: 10,
color: 'black',
fontSize: 18
},
thumb: {
width: width,
height: width * 0.56,
resizeMode: 'cover'
},
play: {
position: 'absolute',
bottom: 100,
right: 14,
width: 46,
height: 46,
paddingTop: 8,
paddingLeft: 18,
backgroundColor: 'transparent',
borderColor: 'black',
borderWidth: 0.5,
borderRadius: 23,
},
cellFooter: {
flexDirection: 'row',
justifyContent: 'space-between',
backgroundColor: '#dddddd',
},
footerBox: {
padding: 10,
flexDirection: 'row',
backgroundColor: 'white',
flex: 1,
marginLeft: 1,
justifyContent: 'center',
},
boxIcon: {
fontSize: 22,
color: '#333',
},
boxText: {
fontSize: 18,
color: '#333',
paddingLeft: 12,
marginTop: 2
},
loadingMore: {
marginVertical: 20
},
loadingText: {
fontSize: 18,
color: '#777',
textAlign: 'center'
}
});
演示示例:
封裝自定義Cell
從上面的程式碼中,我們可以看出,僅僅這麼low的一個頁面,包括css樣式,js邏輯程式碼和render html介面都在list.js檔案中,程式碼300多行,顯得十分混亂,那麼我們能不能把cell進行抽取呢?
List檔案下建立Item.js檔案
Item.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, {Component} from 'react';
import {
Platform,
StyleSheet,
Text,
View,
TouchableOpacity,
Image
} from 'react-native';
import Dimensions from 'Dimensions';
const {width, height} = Dimensions.get('window');
import Icon from 'react-native-vector-icons/Ionicons';
export default class item extends Component {
constructor(props) {
super(props);
this.state = {
rowData: this.props.rowData,
}
}
render() {
let rowData = this.state.rowData;
return (
<TouchableOpacity>
{/*整個Cell*/}
<View style={styles.cellStyle}>
{/*標題文字*/}
<Text style={styles.title}>{rowData.text}</Text>
<Image style={styles.thumb} source={{uri: rowData.profile_image}}>
</Image>
<Icon name="ios-play"
size={30}
style={styles.play}
/>
{/*點贊&評論*/}
<View style={styles.cellFooter}>
{/*點贊*/}
<View style={styles.footerBox}>
<Icon name="ios-heart-outline"
size={30}
style={styles.boxIcon}
/>
{/*點贊文字*/}
<Text style={styles.boxText}>點贊</Text>
</View>
{/*評論*/}
<View style={styles.footerBox}>
<Icon name="ios-chatbubbles-outline"
size={30}
style={styles.boxIcon}
/>
{/*點贊文字*/}
<Text style={styles.boxText}>評論</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
cellStyle: {
width: width,
marginTop: 10,
backgroundColor: 'white',
},
title: {
padding: 10,
color: 'black',
fontSize: 18
},
thumb: {
width: width,
height: width * 0.56,
resizeMode: 'cover'
},
play: {
position: 'absolute',
bottom: 100,
right: 14,
width: 46,
height: 46,
paddingTop: 8,
paddingLeft: 18,
backgroundColor: 'transparent',
borderColor: 'black',
borderWidth: 0.5,
borderRadius: 23,
},
cellFooter: {
flexDirection: 'row',
justifyContent: 'space-between',
backgroundColor: '#dddddd',
},
footerBox: {
padding: 10,
flexDirection: 'row',
backgroundColor: 'white',
flex: 1,
marginLeft: 1,
justifyContent: 'center',
},
boxIcon: {
fontSize: 22,
color: '#333',
},
boxText: {
fontSize: 18,
color: '#333',
paddingLeft: 12,
marginTop: 2
}
});
list.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, {Component} from 'react';
import {
Platform,
StyleSheet,
Text,
View,
ListView,
TouchableOpacity,
Image,
ActivityIndicator,
RefreshControl
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import Dimensions from 'Dimensions';
const {width, height} = Dimensions.get('window');
import config from '../common/config';
import request from '../common/request';
import Item from './Item';
// Mockjs 解析隨機的文字
import Mock from 'mockjs';
let cachedResult = {
nexPage: 1,
item: [],
total: 0,
}
export default class list extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (r1, r2)=>r1 !== r2,
}),
isLoadingTail: false, //沒有載入資料
isRefreshing: false
}
}
//即將顯示
componentWillMount() {
//載入本地資料
// this.dsfetchData();
}
componentDidMount() {
//載入網路資料
this._fetchData(1);
}
_fetchData(page) {
if (page !== 0) {
this.setState({
isLoadingTail: true
});
} else {
this.setState({
isRefreshing: true
});
}
this.setState({
isLoadingTail: true
});
//傳送網路請求
request.get(config.api.base + config.api.list, {
accessToken: '001',
a: 'list',
c: 'data',
type: 29,
page: page
}).then(
(data) => {
//將伺服器得到的資料快取進來
let items = cachedResult.item.slice();
// let items = cachedResult.item.concat(data.list);//把快取的資料進行拼接
if (page !== 0) {//載入更多
items = items.concat(data.list);
cachedResult.nexPage += 1;
} else {//重新整理資料
items = data.list.concat(items);
}
//最後儲存資料
cachedResult.item = items;
cachedResult.total = items.total;
// console.log(items);
// console.log('總資料是:' + cachedResult.total);
// console.log('當前到了第:' + cachedResult.item.length + '個!');
if (page !== 0) {//載入更多
this.setState({
dataSource: this.state.dataSource.cloneWithRows(
cachedResult.item
),
isLoadingTail: false
});
}else {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(
cachedResult.item
),
isRefreshing: false
});
}
}
).catch(//如果有錯
(error) => {
if(page !=0){
this.setState({
isLoadingTail:false
})
}else {
this.setState({
isRefreshing:false
})
}
console.log('err' + error);
}
)
}
render() {
return (
<View style={styles.container}>
{/*導航條*/}
<View style={styles.header}>
<Text style={styles.headerText}>
視訊列表
</Text>
</View>
{/*列表頁面*/}
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
style={styles.listView}
onEndReached={this._fetchMoreData}//滾到底部載入更多資料
onEndReachedThreshold={20} //距離底部還有多遠觸發
renderFooter={this._renderFooter}
refreshControl={
<RefreshControl
refreshing={this.state.isRefreshing}
onRefresh={this._onRefresh}
/>
}
/>
</View>
);
}
//下拉重新整理
_onRefresh = ()=> {
if (!this._hasMore() || this.state.isRefreshing) {
return
}
this._fetchData(0);
}
//自定義Footer檢視
_renderFooter = ()=> {
if (!this._hasMore() && cachedResult.total !== 0) {
return (
<View style={styles.loadingMore}>
<Text style={styles.loadingText}>沒有更多資料啦...</Text>
</View>
)
}
// if(!this.state.isLoadingTail){
// return(<View></View>)
// }
//顯示一朵小菊花
return (
<ActivityIndicator style={styles.loadingMore}></ActivityIndicator>
)
}
//思路:有多種解決方案
//1.傳送請求 2.儲存請求引數 3.對比引數
//重新整理請求 2.儲存request = refresh 3.對比 refresh==儲存request 狀態機中的page值
//狀態機 loading = refresh
//上拉載入更多資料
_fetchMoreData = ()=> {
//判斷是否有更多資料或者是否在請求中
if (!this._hasMore || this.isLoadingTail) {
return
}
let page = cachedResult.nexPage;
//去伺服器去載入更多資料
this._fetchData(page);
}
//判斷是否還有更多資料
_hasMore() {
return cachedResult.item.length !== cachedResult.total;
}
//返回item
//下劃線代表內部類自己用的函式,屬於規範
_renderRow = (rowData)=> {
return (
<Item rowData={rowData} />
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
header: {
// marginTop:Platform.OS == 'ios'?20:0,
paddingTop: 25,
paddingBottom: 15,
backgroundColor: '#dddddd',
borderBottomWidth: 0.5,
borderBottomColor: 'black',
},
headerText: {
fontWeight: '600',
textAlign: 'center',
fontSize: 16,
},
listView: {
},
loadingMore: {
marginVertical: 20
},
loadingText: {
fontSize: 18,
color: '#777',
textAlign: 'center'
}
});
程式碼抽取很簡單,可以詳細看看抽取了哪些程式碼。