React Native——ListView的使用详解

2018-02-27 11:15:15来源:https://juejin.im/post/5a8f7f586fb9a063417b3eea作者:稀土掘金人点击

分享
ListView是什么

ListView - 一个核心组件,用于高效地显示一个可以垂直滚动的变化的数据列表。最基本的使用方式就是创建一个 ListView.DataSource 数据源,然后给它传递一个普通的数据数组,再使用数据源来实例化一个 ListView 组件,并且定义它的 renderRow 回调函数,这个函数会接受数组中的每个数据作为参数,返回一个可渲染的组件(作为listview的每一行)


简单说它就是一个列表组件,用来显示列表视图,类似Android中的ListView,iOS中的UITableView。作为列表组件,ListView是非常常用的。虽然官方文档中指出ListView已经过期,指定FlatList和SectionList替代ListView来使用,但我认为还是有必要对这个控件加深理解,在很多情况下使用ListView还是很方便的。


ListView的相关属性

这里我只列出平常会用的比较多的重要属性加以讲解,其它属性可以查阅官方文档。


dataSource ——ListView.DataSource实例,用来设置列表数据源


renderRow ——方法,用来渲染列表中每一行,该方法有四个参数(rowData, sectionID, rowID, highlightRow),可以通过传递参数来设置每行的数据,区分不同的section和row。四个参数分别表示数据源中一条数据,分组的ID,行的ID,以及标记是否是高亮选中的状态信息。


有了dataSource和renderRow这两个属性,我们就可以完成一个ListView视图了,这两个属性是ListView最基本的属性,是必须要设置的。


renderHeader ——渲染头部视图,以iOS为例,UITableView有一个tableHeaderView属性,可以设置头部视图,这里renderHeader也可以渲染一个自定义的头部视图。


renderFooter ——渲染底部视图,类似于UITableView组件的tableFooterView,可以设置一个尾部视图。


renderSectionHeader ——为每个section渲染一个粘性的头部视图,类似于UITableView中的sectionHeader视图。


stickySectionHeadersEnabled ——sectionHeader是否支持粘性,实际就是是否支持ListView在滑动时当前sectionHeader悬停在上方。这个效果跟UITableView中plain样式的sectionHeader效果一样。


renderSeparator ——渲染每行的分割线,通常我不习惯这么做,可以直接在renderRow中给行视图底部设置一个宽度,使用borderBottomWidth和borderColor来实现分割线。


onEndReached ——当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素的距离时调用。这个方法就是我们用来做列表的上拉加载时用到的。


onEndReachedThreshold ——这个属性在ListView中是数值,当列表被滚动到底部不足这个值的距离时会触发onEndReached中的方法。在FlatList和SectionList中这个值是比值0~1之间,这里需要区分清楚。


ListView的用法

本篇文章我们使用ListView实现三种不同的列表视图,基本可以涵盖我们日常使用ListView的情况。


第一种,简单列表视图的实现



如图,要实现这样一个列表,我们只需要设置数据源dataSource和renderRow就可以了。


在使用dataSource时,我们需要先new一个dataSource对象,对于单个section的列表,使用 rowHasChanged (prevRowData, nextRowData)和 cloneWithRows (dataBlob, rowIdentities)来构造数据源。


对于每行视图,我们需要一个image组件来显示图片,一个Text组件显示文本,使用TouchableOpacity组件将图片和文本包裹起来,并给它设置点击事件,renderRow就完成了。


代码如下:


constructor(props) {
super(props);
let ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(data)
}
}
render() {
return (
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
/>
</View>
)
}
_renderRow = (rowData) => {
return (
<TouchableOpacity style={styles.cellContainer} onPress={() => {}}>
<Image source={rowData.image} style={styles.image}/>
<Text style={styles.title}>{rowData.title}</Text>
</TouchableOpacity>
)
}

其中data是一个包含图片和title的数组。通过设置数据源和渲染行视图的方式我们就得到了一个简单的列表。


第二种,网格形式的列表



要实现这样一个Grid Layout的视图,其实很简单,只需要修改下上面的代码。将ListView内容的样式改为横向布局(flexDireaction:'row'),然后支持换行(flexWrap:'wrap'),renderRow中渲染的元素就会横向排列,如果所有元素一行显示不了就自动换行。


render() {
return (
<View style={{ flex: 1}}>
<ListView
contentContainerStyle={styles.listView}
dataSource={this.state.dataSource}
renderRow={this._renderRow}
/>
</View>
)
}

ListView是继承于ScrollView的,所以这里我们可以给它设置内容样式,注意是contentContainerStyle而不是style。


listView: {
flexDirection:'row',
flexWrap:'wrap',
justifyContent:'space-between',
paddingLeft: 20,
paddingRight: 20,
},

然后调整renderRow中图片、文字和容器的样式,一个GridLayout的列表就完成了。


第三种,分组视图,包含单列和网格样式的列表


假设我们有这样一个列表,有好几个分组(section),每个section中的row显示样式不同,有的是单列的,有的是网格形式的。这时候我们就不能简单的像GridLayout那样将ListView的内容样式改变,因为ListView的内容样式改变后里面的所有元素都受到影响,要么全部纵向显示,要么全部横向显示,达不到我们想要的效果。这里有两种方法:


对于网格元素的section,此section数据源我们给它设置为只有一条数据的数组,在renderRow中使用一个View将所有子元素包裹起来,使用数组的map方法循环显示网格中的子元素。View的内容样式同GridLayout一样横向排列显示然后换行。分组的ListView数据源格式为

{ sectionID_1: [ rowData1, rowData2, ... ],sectionID_2: [rowData], sectionID_3: [ rowData1, rowData2, ... ], ...}

或者

[[ rowData1, rowData2, ... ], [ rowData], [ rowData1, rowData2, ... ], ...]

其中sectionID_2的分组就是要用来显示网格视图的。


在要显示网格元素的section中再嵌入一个ListView,方法同GridLayout一样。

以上两种方式其实原理一致,只不过使用控件不同。这部分的实现相对来说稍微复杂点,在Demo中我分了两种情况实现,具体逻辑请看demo。


这里需要说明的是,对于多个section的ListView和单一section的ListView,dataSource的实现是不一样的。


单一section
let ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {dataSource: ds.cloneWithRows(data)}
多个section
let ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2
});this.state = {dataSource: ds.cloneWithRowsAndSections(data)}

效果图如下



Demo地址: github.com/mrarronz/re…


最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台