如何迈出第四步(数据展示)

数据的获取与展示是每个App必不可少的功能,最常见的就是列表(iOS的TableView,Android的ListView)。在RN中当然也有ListView,第四步将在第三步建立好衔接的基础下,完成2个列表视图的构建,并加入网络请求知识(也就有下拉刷新了)。首先,我们去寻找一个接口,用以获取数据。这里为了方便直接使用聚合,我这里是找了一个微信文章推荐的接口:

1
http://v.juhe.cn/weixin/query?pno=1&ps=20&key=1cb0962fb0fb73dae41d96e88733ac96

参数:

  1. pno:页码
  2. ps:每页内容个数
  3. key:我的key,上边的可以直接拿去使用

接下来继续打开项目,进行编码,首先我们需要的是获取数据进行展示,在News.js中。

在使用ListView的时候,也需要数据源,并且我们还想加一个下拉刷新,我们的数据格式是这样的:

1
2
3
4
5
6
7
8
{
"firstImg":"http:\/\/zxpic.gtimg.com\/infonew\/0\/wechat_pics_-8774101.jpg\/640",
"id":"wechat_20151202064749",
"source":"果果帮",
"title":"笑死了,哈哈小伙子你很有前途",
"url":"http:\/\/v.juhe.cn\/weixin\/redirect?wid=wechat_20151202064749",
"mark":""
}

我们先来尝试着获取数据,使用fetch:

1
fetch('http://v.juhe.cn/weixin/query?pno=1&ps=50&key=1cb0962fb0fb73dae41d96e88733ac96')

然后我们尝试着把数据渲染上去(等下的解释我都写到代码注释里):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import React, { Component, } from 'react'
import { View, Text, ListView, RefreshControl, TouchableOpacity, StyleSheet, Image, Dimensions } from 'react-native'
import { Router, Scene, Actions, ActionConst } from 'react-native-router-flux';
class News extends Component {
constructor(props){
super(props);
// 每一个ListView都需要一个数据源,称为`DataSource`。rowHasChanged用来比较前后的数据是否相同,相同则不重新渲染。
this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1.url !== r2.url });
// 先初始化一个空的数组,供存放列表数据
this._data = [];
// 可变状态
this.state = {
// dataSource是ListView显示的一个必须属性,cloneWithRows用于传入数据。
dataSource: this.ds.cloneWithRows(this._data),
// 控制下拉刷新的状态
isRefreshing: false
};
// 加载第一次数据
this._onRefresh();
}
// 触发下拉刷新
_onRefresh = () => {
// 首先更改状态为true
this.setState({isRefreshing: true});
var self = this;
// 开启网络请求
fetch('http://v.juhe.cn/weixin/query?pno=1&ps=20&key=1cb0962fb0fb73dae41d96e88733ac96')
// 对数据做处理,这里没有写catch,应该写的,捕获错误
.then((response) => response.json())
.then((responseJson) => {
// 看一下是否是成功,成功:先清空本地数据,然后将新的数据加入。因为是下拉刷新,上拉加载的功能自行研究。已经很简单了。
if (responseJson.reason == 'success') {
let data = responseJson.result.list;
if (data) {
this._data = data;
}
}
self.setState({
dataSource: this.ds.cloneWithRows(this._data),
isRefreshing: false
});
})
}
// 用来渲染行的方法,将数据转为Cell进行展示。
_renderRow (rowData, sectionID, rowID) {
return (
<TouchableOpacity
onPress={()=>this._pressRow(rowData)}>
<View style={styles.cell}>
<Image
style={styles.cellImage}
source={{ uri: rowData.firstImg }} />
<View style={styles.cellColumn}>
<Text style={styles.cellText} numberOfLines={0}>{rowData.title}</Text>
<Text style={styles.cellExtra}>#{rowData.source}</Text>
</View>
</View>
</TouchableOpacity>
);
}
_pressRow = (rowData) => {
// 在这里实现点击事件
console.log(rowData.url);
}
render() {
return (
<View style={{ flex: 1, marginTop: 44}}>
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow.bind(this)}
refreshControl={
<RefreshControl
refreshing={this.state.isRefreshing}
onRefresh={this._onRefresh}
tintColor="rgb(181, 181, 181)"
title="下拉刷新数据"
titleColor="rgb(181, 181, 181)"
colors={['rgb(181, 181, 181)', 'rgb(181, 181, 181)', 'rgb(181, 181, 181)']}
/>
}
/>
</View>
)
}
}
const contentWidth = Dimensions.get('window').width;
const styles = StyleSheet.create({
cell: {
padding: 10,
paddingTop: 10,
paddingBottom: 0,
flexDirection: 'row',
flex: 1,
alignItems: 'center'
},
cellImage: {
width: 50,
height: 50,
backgroundColor: 'rgb(221, 221, 221)'
},
cellText: {
textAlign: 'left',
marginLeft: 10,
width: contentWidth - 75,
},
cellExtra: {
fontSize: 10,
color: 'rgb(181, 181, 181)',
marginLeft: 10
},
cellColumn: {
flexDirection: 'column'
}
});
export default News

接着我们来实现点击事件的详情跳转(这里有个问题,就是TabBar的隐藏,因为我们是用Router来跳转的,而在实现的时候,TabBar我们是自己写的,然后react-router-flux有个问题就是不能在子路由中打开父路由的界面,暂时提了一个issue,还有一个暂时的解决方案,所有的外层都用router-flux提供。这里先做演示),在NewsPage.js中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React, { Component, } from 'react'
import { Router, Scene } from 'react-native-router-flux';
import News from './News'
import NewsDetail from './NewsDetail';
class NewsPage extends Component {
render() {
return (
<Router>
<Scene key="_NewsRouter">
<Scene
key="News"
component={News}
title="News"
navigationBarStyle={{ backgroundColor: 'white' }}
titleStyle={{color: '#525354', fontFamily: 'Helvetica', fontSize: 20}}
initial={true} />
<scene key="NewsDetail" component={NewsDetail} title="NewsDetail" hideTabBar />
</Scene>
</Router>
)
}
}
export default NewsPage

然后在点击的时候打开详情:

1
2
3
4
5
6
_pressRow = (rowData) => {
Actions.NewsDetail({
url: rowData.url,
type: ActionConst.PUSH
});
}

个人中心里边就比较简单了,大家自己通过之前的学习去尝试着完成微信个人中心界面的布局。简单的实现几个点击事件。TabBar隐藏的问题需要我们修改视图的层级:把TabBar变为一个Navigation包涵的视图。首先去掉index.ios.js中:

1
<Scene key="RootPage" component={RootPage} title="News" initial={this.props.launched}/>

原来的hideNavBar属性,并直接改名为News。然后把News.js中的代码迁移到NewsPage中。最后的index.ios.js是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React, { Component } from 'react';
import { AppRegistry } from 'react-native';
import { Router, Scene } from 'react-native-router-flux';
import GuidePage from './GuidePage';
import RootPage from './RootPage';
import NewsDetail from './News/NewsDetail';
class Project extends Component {
render() {
return (
<Router>
<Scene key="root">
<Scene key="GuidePage" component={GuidePage} hideNavBar title="GuidePage" initial={this.props.launched} />
<Scene key="RootPage" component={RootPage} title="News" initial={this.props.launched}/>
<scene key="NewsDetail" component={NewsDetail} title="NewsDetail"/>
</Scene>
</Router>
);
};
};
AppRegistry.registerComponent('Project', () => Project);

然后就可以解决跳转的问题。但是接着出现了下一个问题,在点击个人中心的之后,发现顶部导航还是News的名称,所以TabBar也要进行处理,由于react-router-flux目前的框架问题(子路由不可以操作父路由,并且想操作Tabbar只能使用router-flux的方式创建TabBar),所以我们直接在index中使用Scene创建我们的Tabbar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import React, { Component } from 'react';
import { AppRegistry, Text, StyleSheet, View, Image } from 'react-native';
import { Router, Scene } from 'react-native-router-flux';
import GuidePage from './GuidePage';
import NewsDetail from './News/NewsDetail';
import NewsPage from './News/NewsPage';
import AccountPage from './Account/AccountPage';
class TabIcon extends React.Component {
render(){
let selectedColor = '#34A3FF';
let deSelectedColor = 'rgb(181, 181, 181)';
let title = this.props.title;
let selected = this.props.selected;
let itemImage = {
"新闻": {
selected: 'http://ocef2grmj.bkt.clouddn.com/first_selected.png',
deSelected: 'http://ocef2grmj.bkt.clouddn.com/first_normal.png'
},
"我的": {
selected: 'http://ocef2grmj.bkt.clouddn.com/fourth_selected.png',
deSelected: 'http://ocef2grmj.bkt.clouddn.com/fourth_normal.png'
}
};
return (
<View style={styles.tabBarItem}>
<Image source={{ uri: selected? itemImage[title].selected : itemImage[title].deSelected}} style={styles.tabBarImage}/>
<Text style={[{color: selected ? selectedColor :deSelectedColor}, styles.tabBatText]}>{title}</Text>
</View>
);
}
}
class Project extends Component {
render() {
return (
<Router>
<Scene key="GuidePage" component={GuidePage} hideNavBar title="GuidePage" initial={this.props.launched} />
<Scene key="main" tabs={true} style={styles.tabBarStyle} initial={true}>
<Scene key="NewsModule" title="新闻" icon={TabIcon} initial={true}>
<Scene key="News" component={NewsPage} title="News" />
<Scene key="NewsDetail" component={NewsDetail} hideTabBar title="NewsDetail"/>
</Scene>
<Scene key="AccountModule" title="我的" icon={TabIcon}>
<Scene key="Mine" component={AccountPage} title="个人中心" />
</Scene>
</Scene>
</Router>
);
};
};
const styles = StyleSheet.create({
tabBarStyle: {
borderTopWidth : .5,
borderColor : '#b7b7b7',
backgroundColor: 'white',
opacity : 1
},
tabBarItem: {
alignItems: 'center'
},
tabBatText: {
fontSize: 10,
marginTop: 4
},
tabBarImage: {
width: 26,
height: 26
}
});
AppRegistry.registerComponent('Project', () => Project);

最终的效果是:

跳转到详情的时候会自动的隐藏TabBar,这里还需要一个工作就是把TabBarItem作为我们自己的一个控件,交给大家啦!到此为止,React-Native的上手教程就结束了,希望大家有所收获,进阶的内容一起讨论,我会在一个自己的私有项目中使用React-Native来做一个核心的模块,之后会发布出来。

代码地址依旧不变,以后还会更新呦。

评论