您的位置:首页 > Web前端 > React

React-Native新列表组件FlatList和SectionList学习 | | 联动列表实现

2017-06-27 20:01 936 查看
React-Native在0.43推出了两款新的列表组件:FlatList(高性能的简单列表组件)和SectionList(高性能的分组列表组件).
http://www.cnblogs.com/shaoting/p/7069312.html
从官方上它们都支持常用的以下功能:
完全跨平台。
支持水平布局模式。
行组件显示或隐藏时可配置回调事件。
支持单独的头部组件。
支持单独的尾部组件。
支持自定义行间分隔线。
支持下拉刷新。
支持上拉加载。

其中,SectionList适合分组/类/区,但是在0.43版本中,如果希望section的头部能够吸顶悬浮,请暂时先使用老版的
<ListView>.


它们都是基于
<VirtualizedList>
组件的封装(不同于ListView,ListView是继承自ScrollView,这意味着ListView可以使用所有ScrollView的属性,但是不带重用,性能稍微不足,也就是说FlatList.SectionList这两款组件和ListView,ScrollView没啥关系,而ListView和ScrollView是父子关系),.所以需要注意几点.详细的请在官方浏览.其中有一点必须注意:在使用时,默认情况下每行都需要提供一个不重复的key属性.也可以提供一个
keyExtractor
函数来动态绑定数据源中的id等其他不唯一的数据。如果不绑定会报一个警告:



 

接下里使用这两个组件写一个demo:列表组件的联动(ps:其实个人感觉使用ListView实现更加方便.也更易扩展)

数据源我们采用本地数据:



{
"food_spu_tags":[

{
"title":"1",
"data":[
{
"name":"一 nghnh",
"key":"1"
},
{
"name":"一 tyui22uyt",
"key":"2"
},
{
"name":"一 3fdsfdga",
"key":"3"
}
]
},
{
"title":"2",
"data":[
{
"name":"二 fsd",
"key":"4"
},
{
"name":"二 gfdh",
"key":"5"
},
{
"name":"二 ghdsfd",
"key":"6"
},
{
"name":"二 hkjhg",
"key":"7"
},
{
"name":"二 oiuytre",
"key":"8"
},
{
"name":"二 phfd",
"key":"9"
}
]
},
{
"title":"3",
"data":[
{
"name":"三 pknbv",
"key":"10"
},
{
"name":"三 qazxsef",
"key":"11"
},
{
"name":"三 plmnbgf",
"key":"12"
},
{
"name":"三 ggggg",
"key":"13"
},
{
"name":"三  gfd",
"key":"14"
},
{
"name":"三 fgh",
"key":"15"
},
{
"name":"三 hhf",
"key":"16"
},
{
"name":"三 jff",
"key":"17"
},
{
"name":"三 sfgd",
"key":"18"
},
{
"name":"三 dffhsd",
"key":"19"
},
{
"name":"三 ghd",
"key":"20"
},
{
"name":"三 ghsg",
"key":"21"
}
]
},
{
"title":"4",
"data":[
{
"name":"四 ghs",
"key":"22"
},
{
"name":"四 hth",
"key":"23"
}
]
},
{
"title":"5",
"data":[
{
"name":"五 teh",
"key":"24"
},
{
"name":"五 thtr",
"key":"25"
},
{
"name":"五 thereth",
"key":"26"
},
{
"name":"五 yefdgs",
"key":"27"
},
{
"name":"五 htweh",
"key":"28"
},
{
"name":"五 thrhwt",
"key":"29"
},
{
"name":"五 geheht",
"key":"30"
},
{
"name":"五 thwtw",
"key":"31"
}
]
},
{
"title":"6",
"data":[
{
"name":"六 thsfsg",
"key":"32"
},
{
"name":"六 thwfs",
"key":"33"
},
{
"name":"六 htsfd",
"key":"34"
}
]
},
{
"title":"7",
"data":[
{
"name":"七 hgshfd",
"key":"35"
}
]
},
{
"title":"8",
"data":[
{
"name":"八 rgdsgsfd",
"key":"36"
},
{
"name":"八 grht",
"key":"37"
},
{
"name":"八 htrfss",
"key":"38"
},
{
"name":"八 thsgfd",
"key":"39"
},
{
"name":"八 hthe",
"key":"40"
},
{
"name":"八 trgtsf",
"key":"41"
},
{
"name":"八 f45f",
"key":"42"
},
{
"name":"八 4qtq",
"key":"43"
},
{
"name":"八 43f",
"key":"44"
},
{
"name":"八 43ff",
"key":"45"
},
{
"name":"八 45gwrsfd",
"key":"46"
}
]
},
{
"title":"9",
"data":[
{
"name":"九 43qgf",
"key":"47"
},
{
"name":"九 ref3",
"key":"48"
},
{
"name":"九 54sf",
"key":"49"
}
]
},
{
"title":"10",
"data":[
{
"name":"十 43refsd",
"key":"50"
},
{
"name":"十 43refzd",
"key":"51"
},
{
"name":"十 4q3gfd",
"key":"52"
},
{
"name":"十 wgf",
"key":"53"
},
{
"name":"十 4q3fs",
"key":"54"
}
]
},
{
"title":"11",
"data":[
{
"name":"十一 wrf",
"key":"55"
},
{
"name":"十一 5ersf",
"key":"56"
},
{
"name":"十一 43fs",
"key":"57"
},
{
"name":"十一 43fs",
"key":"58"
},
{
"name":"十一 5gs",
"key":"59"
},
{
"name":"十一 w5gfsd",
"key":"60"
},
{
"name":"十一 4qrgfs",
"key":"61"
}
]
},
{
"title":"12",
"data":[
{
"name":"十二 4wgfsd",
"key":"62"
},
{
"name":"十二 w5gfsd",
"key":"63"
},
{
"name":"十二 4qgfsgf",
"key":"64"
},
{
"name":"十二 3qgsf",
"key":"65"
}
]
}
]
}


1.新建个主类放置左右两个列表组件(左边的FlatList右边的SectionList)



/**
* Sample React Native App
* https://github.com/facebook/react-native * @flow
*/

import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import LeftFlatList from './leftFlatList'
import RightSectionList from './RightSectionList'
import linkageData from './linkage.json'
export default class Main extends Component {
render() {
return (
<View style={{flexDirection:'row'}}>
<LeftFlatList data = {linkageData}/>
<RightSectionList data = {linkageData}/>
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});


2.左边的FlatList,key采用keyExtractor函数绑定,就是数据源中title.



/**
* Created by shaotingzhou on 2017/6/22.
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Image,
TouchableOpacity,
Platform,
Dimensions,
RefreshControl,
FlatList,
ActivityIndicator,
DeviceEventEmitter,
ScrollView
} from 'react-native';
var {width,height} = Dimensions.get('window');
var dataAry = []

export default class LeftFlatList extends Component{
// 构造
constructor(props) {
super(props);
dataAry = this.props.data.food_spu_tags
this.state = {
dataAry: dataAry,
cell:0  //默认选中第一行
};
}
render() {
return (
<FlatList
ref='FlatList'
style={{width:80}}
data = {this.state.dataAry} //数据源
renderItem = {(item) => this.renderRow(item)} //每一行render
ItemSeparatorComponent = {()=>{return(<View style={{height:1,backgroundColor:'cyan'}}/>)}} //分隔线
keyExtractor={this.keyExtractor}  //使用json中的title动态绑定key
/>
);
}
//使用json中的title动态绑定key
keyExtractor(item: Object, index: number) {
return item.title
}
//每一行render
renderRow =(item) =>{
return(
<TouchableOpacity onPress={()=>this.cellAction(item)}>
<View style={{height:60,flexDirection:'row',alignItems:'center'}}>
<View style={{height:50,width:5,backgroundColor: item.index == this.state.cell ? 'red' : 'rgba(0,0,0,0)'}}/>
<Text style={{marginLeft:20}}>{item.item.title}</Text>
</View>
</TouchableOpacity>
)
}
//点击某行
cellAction =(item)=>{
// alert(item.index)
if(item.index < this.state.dataAry.length - 1){
this.setState({
cell:item.index
})
DeviceEventEmitter.emit('left',item.index); //发监听
}

}

componentWillUnmount(){
// 移除监听
this.listener.remove();
}

componentWillMount() {
this.listener = DeviceEventEmitter.addListener('right',(e)=>{
this.refs.FlatList.scrollToIndex({animated: true, index: e-1})
this.setState({
cell:e-1
})
});
}

};

var styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
}
});


3.右边的SectionList,key采用数据源中id来绑定.



/**
* Created by shaotingzhou on 2017/6/22.
*/
import React, {Component} from 'react';
import {
StyleSheet,
View,
Text,
SectionList,
Dimensions,
DeviceEventEmitter,
ScrollView
} from 'react-native';
var {width,height} = Dimensions.get('window');
var sectionData = []
export default class RightSectionList extends Component {
// 构造
constructor(props) {
super(props);
sectionData = this.props.data.food_spu_tags
this.state = {
sectionData:sectionData
};
}
//行
renderItem = (item) => {
return (
<View style={{height:60,justifyContent:'center',marginLeft:15}}>
<Text>{item.item.name}</Text>
</View>
)
}
//头
sectionComp = (section) => {
return (
<View style={{height:30,backgroundColor:'#DEDEDE',justifyContent:'center',alignItems:'center'}}>
<Text >{section.section.title}</Text>
</View>
)
}

render() {
return (
<SectionList
ref='sectionList'
style={{width:width-80}}
renderSectionHeader={(section)=>this.sectionComp(section)} //头
renderItem={(item)=>this.renderItem(item)} //行
ItemSeparatorComponent = {()=>{return(<View style={{height:1,backgroundColor:'black'}}/>)}}//分隔线
sections={this.state.sectionData} //数据
onViewableItemsChanged = {(info)=>this.itemChange(info)}  //滑动时调用
/>

);
}

componentDidMount() {
//收到监听
this.listener = DeviceEventEmitter.addListener('left',(e)=>{
// console.log(e + 1) // 左边点击了第几行
// console.log(sectionData) // 数据源
// console.log(sectionData[e])
// console.log(sectionData[e].data.length)
// SectionList实现scrollToIndex需要修改VirtualizedSectionList和SectionList源码
if(e > 0){
//计算出前面有几行
var count = 0
for(var i = 0; i < e; i++){
count += sectionData[i].data.length +1
}
this.refs.sectionList.scrollToIndex({animated: true, index: count})
}else {
this.refs.sectionList.scrollToIndex({animated: true, index: 0})  //如果左边点击第一行,右边则回到第一行
}

});
}

componentWillUnmount(){
// 移除监听
this.listener.remove();
}

itemChange = (info)=>{
let title = info.viewableItems[0].item.title
var reg = new RegExp("^[0-9]*$");
if (reg.test(title)) {
DeviceEventEmitter.emit('right',title); //发监听
}
}

}


其中,使用事件监听来实现点击和滑动的监听.

我们使用scrollToIndex来移动.但是呢,FlatList对VirtualizedList封装的时候有添加这个方法,而SectionList并没有(why?).无奈自己修改下它的源码.

a.在node_modules/react-native/Libraries/Lists/SectionList.js 下修改 250-310行代码为



class SectionList<SectionT: SectionBase<any>>
extends React.PureComponent<DefaultProps, Props<SectionT>, void> {
props: Props<SectionT>;
static defaultProps: DefaultProps = defaultProps;

render() {
const List = this.props.legacyImplementation ? MetroListView : VirtualizedSectionList;
return <List
ref={this._captureRef}
{...this.props} />;
}

_captureRef = (ref) => {
this._listRef = ref;
};

scrollToIndex = (params: { animated?: ?boolean, index: number, viewPosition?: number }) => {
this._listRef.scrollToIndex(params);
}
}


b.在node_modules/react-native/Libraries/Lists/VirtualizedSectionList.js 下的335下面增加

scrollToIndex = (params: { animated?: ?boolean, index: number, viewPosition?: number }) => {
this._listRef.scrollToIndex(params);
}


修改后完整源码见:SectionList.js VirtualizedSectionList.js.

OK.修改完成后就可以实现点击左联右了.

而右联左,通过SectionList的onViewableItemsChanged属性实现.

以后就是关于FlatList和SectionList的学习demo.

再说一遍,实现联动组件最好使用ListView.因为现阶段官方推出的FlatList和SectionList的方法较少,bug较多.



demo源码github:https://github.com/pheromone/RN-FlatList-SectionList
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: