React Native_手把手教你做项目(五.下拉刷新RefreshControl&封装自定义Cell)
程序员文章站
2022-05-30 21:01:46
...
接下来我们继续下拉刷新的功能,主要是缓存数据的拼接与后台服务器的配合。把数据最后的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'
}
});
代码抽取很简单,可以详细看看抽取了哪些代码。