ReactNative学习笔记(五)之ListView

前言

接上一篇ReactNative学习笔记(四)之自定义文本组件与Image组件继续学习ReactNative

ListView

ListView就是列表视图, 一个用来显示垂直滚动的内容列表.它需要两个东西, dataSource, 就是数据源.和renderRow, 用来显示每一行内容用的模板.
首先导入ListView

1
import {Platform, StyleSheet, Text, View, Image, ImageBackground, ListView} from 'react-native';

然后在App这个类中添加一个constructor方法

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
export default class App extends Component<Props> {
constructor(props) {
super(props)
let movies = [
{'title': '肖申克的救赎'},
{'title': '这个杀手不太冷'},
{'title': '阿甘正传'},
{'title': '霸王别姬'},
{'title': '美丽人生'},
{'title': '三傻大闹宝莱坞'},
{'title': '触不可及'},
{'title': '时空恋旅人'},
{'title': '我和狗狗的十个约定'},
];

let dataSource = new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
});

this.state = {
movies: dataSource.cloneWithRows(movies)
};
}
...
}

constructor方法里需要先调用super(props)方法. 我们手工添加一些数据.ListView本身有一种很有效的方法去显示列表的数据, 而且我们要确定重新显示列表内容的时候只重新显示修改过的数据, 所以需要一个对比数据的方法, 也就是rowHasChanged方法.
再去设置组件初始化的状态, 把这个状态作为ListView的数据源.
然后我们再修改App的render方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export default class App extends Component<Props> {
...
render() {
return (
<View
style={styles.container}>
<ListView
dataSource={this.state.movies}
renderRow={
movie => <Text style={styles.itemText}>{movie.title}</Text>
}
/>
</View>
);
}
}

运行效果:

从网络获取数据并重新整理列表的显示

首先需要从网络请求数据, 我们使用的api是这个https://api.douban.com/v2/movie/top250, 在App这个类里面写一个fetchData方法

1
2
3
4
5
6
7
8
9
10
11
fetchData() {
fetch(REQUEST_URL)
.then(response => response.json())
.then(responseData => {
this.setState({
movies: this.state.movies.cloneWithRows(responseData.subjects),
loaded: true
})
})
.done();
}

对了, App的构造函数的的state中需要加一个loaded变量, 用来标记数据是否已经加载完成.在fetchData方法中, 先将数据转json, 然后我们使用的数据是subjects这部分数据作为数据源

在得到数据之后将state中的loaded属性设置为true.
然后我们修改App的render方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
render() {
if(!this.state.loaded) {
return (
<View style={styles.container}>
<View style={styles.loading}>
<Text>加载中...</Text>
</View>

</View>
);
}
return (
<View style={styles.container}>
<ListView
dataSource={this.state.movies}
renderRow={this.renderMovieList}
/>
</View>
);
}

在state为false的时候显示一个加载中的视图, 如果state为true, 那就显示一个ListView, 数据源就是this.state中的movies, 模板就是一个renderMovieList方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
renderMovieList(movie) {
return (
<View style={styles.item}>
<View style={styles.itemImage}>
<Image
source={{uri: movie.images.large}}
style={styles.image}
/>
</View>
<View style={styles.itemContent}>
<Text style={styles.itemHeader}>{movie.title}</Text>
<Text style={styles.itemMeta}>
{movie.original_title} ( {movie.year} )
</Text>
<Text style={styles.redText}>
{movie.rating.average}
</Text>

</View>
</View>
)
}

这里的样式经过整理了一下, 稍微有些繁琐, 代码如下:

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
let styles = StyleSheet.create({
redText: {
color: '#db2828',
fontSize: 15,
},
itemMeta: {
fontSize: 16,
color: 'rgba(0, 0, 0, 0.6)',
marginBottom: 6,
},
itemHeader: {
fontSize: 18,
fontFamily: 'Helvetica Neue',
fontWeight: '300',
color: '#6435c9',
marginBottom: 6,
},
itemContent: {
flex: 1, // todo 这里的flex等于1到底有什么用
marginLeft: 13,
marginTop: 6,
},
item: {
flexDirection: 'row',
borderBottomWidth: 1,
borderColor: 'rgba(100, 53, 201, 0.1)',
paddingBottom: 6,
marginBottom: 6,
flex: 1, //
},
loading: {
flex: 1, // todo 这里的flex等于1到底有什么用
justifyContent: 'center',
alignItems: 'center',
},
...
});

运行效果:

注意如果把itemContent属性中的flex: 1注释掉, flex属性就会变成默认的stretch
效果如下:

可以看到文字跑到外面去了, 这是因为, stretch是伸展、张开的意思, 如果文字太长就会将容纳文字的容器的宽度撑大, 也就是Android里的包裹内容的意思, 一撑就撑到屏幕外面去了, 而如果限制容器的宽度为剩余空间的宽度, 文字就会在到控件边缘的时候自动换行了, 不会超出控件的宽度.这点需要注意.

0%