列表顯示數(shù)據(jù)狈定,基本什么應(yīng)用都是必須。筆者寫(xiě)作的時(shí)候RN版本是0.34习蓬。今天就來(lái)從淺到深的看看React Native的ListView怎么使用纽什。
首先是使用寫(xiě)死的數(shù)據(jù),之后會(huì)使用網(wǎng)絡(luò)請(qǐng)求的數(shù)據(jù)在界面中顯示友雳。最后加上一個(gè)ActivityIndicator稿湿,網(wǎng)絡(luò)請(qǐng)求的過(guò)程中顯示Loading圖標(biāo)铅匹,加載完成之后顯示數(shù)據(jù)押赊,隱藏Loading圖標(biāo)。
最簡(jiǎn)單的
//@flow
import React from 'react';
import {
Text,
View,
ListView
} from 'react-native';
export default class DemoList extends React.Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(['row 1', 'row 2'])
};
}
render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) => <Text>{rowData}</Text>} />
);
}
}
引入所需要的內(nèi)置組件之類(lèi)的就不多說(shuō)了包斑。
第一步流礁,在constructor
里設(shè)置數(shù)據(jù)源,并同時(shí)指定什么時(shí)候重新繪制一行罗丰,就是在這個(gè)時(shí)候(r1, r2) => r1 !== r2}
重繪神帅。
之后,在state里面設(shè)置數(shù)據(jù)源萌抵。下面從網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)的時(shí)候state的作用就更加明顯了找御。RN的組件在state發(fā)生改變的時(shí)候就會(huì)重繪元镀。這個(gè)下面會(huì)詳細(xì)解釋。
最后霎桅,在render
方法里返回ListView栖疑,這里的props里有一個(gè)renderRow
。在這里指定的代碼就是把數(shù)據(jù)源中每一行的數(shù)據(jù)繪制在Text
里滔驶。
一步一步接近實(shí)際產(chǎn)品開(kāi)發(fā)
下面就把繪制行的部分抽象出來(lái)遇革。在Native應(yīng)用的開(kāi)發(fā)中,無(wú)論是iOS還是Android揭糕,行繪制的部分都是單獨(dú)出來(lái)的萝快。在RN里雖然可以不獨(dú)立出來(lái),但是你也看到了著角,這樣的寫(xiě)法遇到稍微復(fù)雜一點(diǎn)的行內(nèi)容的時(shí)候就捉襟見(jiàn)肘了揪漩。不獨(dú)立出來(lái)行繪制部分代碼會(huì)很難維護(hù)。
這部分不復(fù)雜雇寇,獨(dú)立出來(lái)以后是這樣的:
import //...略...
export default class DemoList extends React.Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(['row 1', 'row 2'])
};
//bind
this._renderRow = this._renderRow.bind(this);
}
_renderRow(rowData) {
return (
<View style={{height: 50}}>
<Text>{rowData}</Text>
</View>
);
}
render() {
return (
<ListView dataSource={this.state.dataSource}
renderRow={this._renderRow} />
);
}
}
這個(gè)例子和上例基本上一樣氢拥。只是多了一個(gè)_renderRow(rowData)
方法。
注意:在使用這個(gè)方法以前锨侯,一定要綁定:this._renderRow = this._renderRow.bind(this);
嫩海。綁定也可以這樣<ListView dataSource={this.state.dataSource} renderRow={this._renderRow.bind(this)} />
。
在繪制行的時(shí)候囚痴,比之前稍微有一點(diǎn)改動(dòng)叁怪。行文本的外面套了一個(gè)View,并指定這個(gè)View的高度為50深滚。
加上裝飾
從現(xiàn)在來(lái)看奕谭,數(shù)據(jù)只有兩行。如果不滑動(dòng)一下的話痴荐,看起來(lái)和兩個(gè)上下排列的Text沒(méi)有什么區(qū)別血柳。
首先我們加一個(gè)分割線:
export default class DemoList extends React.Component {
constructor() {
//記得使用方法之前綁定
this._renderSeparator = this._renderSeparator.bind(this);
}
_renderRow(rowData) {
// ...略...
}
_renderSeparator(sectionID: number, rowID: number, adjacentRowHighlighted: bool) {
return (
<View key={`{sectionID}-${rowID}`}
style={{height: 1, backgroundColor: 'black'}}>
</View>
);
}
render() {
return (
<ListView dataSource={this.state.dataSource}
renderRow={this._renderRow}
renderSeparator={this._renderSeparator}
/>
);
}
}
這里需要額外說(shuō)明一些,在方法里_renderSeparator(sectionID: number, rowID: number, adjacentRowHighlighted: bool)
我看看到了在參數(shù)的名稱(chēng)后面都有類(lèi)型的說(shuō)明生兆。這個(gè)不是ES6的也不是js里的难捌,而是FB自己搞的一套靜態(tài)類(lèi)型檢查工具里的定義。這個(gè)工具叫Flow鸦难。
如果你從一開(kāi)始就沒(méi)打算跟flow扯上任何關(guān)系根吁,那么就按照ES標(biāo)準(zhǔn)寫(xiě)就好。
至于分割線也是非常簡(jiǎn)單合蔽。我們這就返回了一個(gè)高度一個(gè)像素的击敌,背景色為黑色的view。
點(diǎn)擊和高亮
Row的點(diǎn)擊不想Native那樣拴事,默認(rèn)的一般就有了沃斤。在RN里圣蝎,我們需要手動(dòng)賦予一行可以被點(diǎn)擊的功能。
_renderRow(rowData: string, sectionID: number, rowID: number, highlightRow: (sectionID: number, rowID: number) => void) {
return (
<TouchableHighlight onPress={() => {
this._pressRow(rowID);
highlightRow(sectionID, rowID);
}}>
<View style={styles.row}>
<Text style={styles.text}>{rowData}</Text>
</View>
</TouchableHighlight>
);
}
在RN里處理一般點(diǎn)擊的不二選擇就是TouchableHighlight
衡瓶。在TouchableHighlight
里的onPress
里調(diào)用自定義的_pressRow方法處理點(diǎn)擊捅彻,highlightRow
方法高亮行。
當(dāng)然鞍陨,這里就少不了用到樣式了:
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
justifyContent: 'center',
padding: 10,
backgroundColor: '#F6F6F6',
},
text: {
flex: 1,
},
seperator: {
height: 1,
backgroundColor: '#CCCCCC'
}
});
把Cell分離
在實(shí)際的開(kāi)發(fā)中步淹,一般沒(méi)有人會(huì)把Row(或者行)的繪制和ListView
放在一起。我們這里就演示如何把Row的繪制分離出去诚撵。
首先創(chuàng)建一個(gè)單獨(dú)的文件缭裆,定義Cell:
import React from 'react';
import {
View,
Text,
TouchableHighlight,
StyleSheet
} from 'react-native';
export default class DemoCell extends React.Component {
render() {
return (
<View>
<TouchableHighlight onPress={this.props.onSelect}>
<View style={styles.row}>
<Text style={styles.text}>{this.props.rowData}</Text>
</View>
</TouchableHighlight>
</View>
);
}
};
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
justifyContent: 'center',
padding: 10,
backgroundColor: '#F6F6F6',
},
text: {
flex: 1,
},
});
Row也是一個(gè)組件,是一個(gè)組件就可以在另外的組建里渲染寿烟。所以澈驼,單獨(dú)定義的Row就是這么用的。
回到demoList.js文件筛武。在_renderRow
方法中修改代碼:
_renderRow(rowData: string, sectionID: number, rowID: number, highlightRow: (sectionID: number, rowID: number) => void) {
return (
// <TouchableHighlight onPress={() => {
// this._pressRow(rowID);
// highlightRow(sectionID, rowID);
// }}>
// <View style={styles.row}>
// <Text style={styles.text}>{rowData}</Text>
// </View>
// </TouchableHighlight>
<DemoCell onSelect={() => {
this._pressRow(rowID);
highlightRow(sectionID, rowID);
}} rowData={rowData}/>
);
}
結(jié)合網(wǎng)絡(luò)請(qǐng)求
ListView在實(shí)戰(zhàn)中缝其,除非是Settings之類(lèi)的界面,數(shù)據(jù)都是從網(wǎng)絡(luò)請(qǐng)求得到的徘六。上一節(jié)中正好已經(jīng)講述了如何使用RN內(nèi)置的fetch請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)内边。這一節(jié)中就是用fetch來(lái)請(qǐng)求dribbble的數(shù)據(jù)。
在使用dribbble的數(shù)據(jù)之前你需要注冊(cè)待锈,獲得Access Token漠其。這是請(qǐng)求認(rèn)證所必須的。
export default class DemoList extends React.Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
isLoading: false,
isLoadingTail: false,
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
filter: this.props.filter,
queryNumber: 0,
};
//...略...
}
//...略...
_getShots(query: string) {
this.setState({
isLoading: true,
queryNumber: this.state.queryNumber + 1,
isLoadingTail: false,
});
api.getShotsByType(query, 1).then((responseData) => {
this.setState({
isLoading: false,
dataSource: this._getDataSource(responseData),
});
}).catch((error) => {
this.LOADING[query] = false;
this.resultsCache.dataForQuery[query] = undefined;
this.setState({
dataSource: this._getDataSource([]),
isLoading: false,
});
});
}
還是在類(lèi)DemoList
里竿音,其他無(wú)關(guān)緊要的代碼先略去和屎。要緊的地方是需要注意在constructor
里設(shè)置state的時(shí)候dataSource
如何設(shè)置的。
state的改變會(huì)影響到組件的繪制春瞬。所以柴信,在_getShots
方法里,開(kāi)始請(qǐng)求之前先設(shè)置一個(gè)默認(rèn)的state狀態(tài)宽气。在請(qǐng)求成功之后使用setState
設(shè)置一個(gè)随常,在catch到異常的時(shí)候再顯示另外一個(gè)。
在state
里還有一個(gè)屬性叫做isLoading: false,
抹竹。這個(gè)是影控制Loading視圖的线罕。在false的時(shí)候隱藏止潮,在true的時(shí)候顯示窃判。
那么loading界面是什么樣呢?
<View style={{alignItems: 'center', justifyContent: 'center', flex: 1, backgroundColor: 'white'}}>
<ActivityIndicator animating={true}
style={[styles.centering]}
size="large"
color="#cccccc"
/>
</View>
組合起來(lái)
在類(lèi)DemoList
里組合相關(guān)代碼:
_renderView() {
if (this.state.isLoading) {
return (
<UNActivityIndicator loadingType={LOADING_TYPE.Large} />
);
}
return (
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
renderSeparator={this._renderSeparator}
automaticallyAdjustContentInsets={false}
/>
</View>
);
}
在renderView的時(shí)候喇闸,先檢查state.isLoading
袄琳,如果需要loading視圖询件,那么返回loading視圖,其他的不返回唆樊。數(shù)據(jù)加載成功之后state.isLoading
被設(shè)置為false宛琅,那么顯示ListView。
填坑完畢
以上就是處理ListView和其中的Cell的一些常見(jiàn)問(wèn)題的方法逗旁。