列表顯示數據,基本什麼應用都是必須。筆者寫做的時候RN版本是0.34。今天就來從淺到深的看看React Native的ListView怎麼使用。react
首先是使用寫死的數據,以後會使用網絡請求的數據在界面中顯示。最後加上一個ActivityIndicator,網絡請求的過程當中顯示Loading圖標,加載完成以後顯示數據,隱藏Loading圖標。react-native
//@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>} /> ); } }
引入所須要的內置組件之類的就很少說了。api
第一步,在constructor
裏設置數據源,並同時指定何時從新繪製一行,就是在這個時候(r1, r2) => r1 !== r2}
重繪。網絡
以後,在state裏面設置數據源。下面從網絡請求數據的時候state的做用就更加明顯了。RN的組件在state發生改變的時候就會重繪。這個下面會詳細解釋。工具
最後,在render
方法裏返回ListView,這裏的props裏有一個renderRow
。在這裏指定的代碼就是把數據源中每一行的數據繪製在Text
裏。fetch
下面就把繪製行的部分抽象出來。在Native應用的開發中,不管是iOS仍是Android,行繪製的部分都是單獨出來的。在RN裏雖然能夠不獨立出來,可是你也看到了,這樣的寫法遇到稍微複雜一點的行內容的時候就捉襟見肘了。不獨立出來行繪製部分代碼會很難維護。flex
這部分不復雜,獨立出來之後是這樣的:this
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} /> ); } }
這個例子和上例基本上同樣。只是多了一個_renderRow(rowData)
方法。code
注意:在使用這個方法之前,必定要綁定:this._renderRow = this._renderRow.bind(this);
。綁定也能夠這樣<ListView dataSource={this.state.dataSource} renderRow={this._renderRow.bind(this)} />
。開發
在繪製行的時候,比以前稍微有一點改動。行文本的外面套了一個View,並指定這個View的高度爲50。
從如今來看,數據只有兩行。若是不滑動一下的話,看起來和兩個上下排列的Text沒有什麼區別。
首先咱們加一個分割線:
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} /> ); } }
這裏須要額外說明一些,在方法裏_renderSeparator(sectionID: number, rowID: number, adjacentRowHighlighted: bool)
我看看到了在參數的名稱後面都有類型的說明。這個不是ES6的也不是js裏的,而是FB本身搞的一套靜態類型檢查工具裏的定義。這個工具叫Flow。
若是你從一開始就沒打算跟flow扯上任何關係,那麼就按照ES標準寫就好。
至於分割線也是很是簡單。咱們這就返回了一個高度一個像素的,背景色爲黑色的view。
Row的點擊不想Native那樣,默認的通常就有了。在RN裏,咱們須要手動賦予一行能夠被點擊的功能。
_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裏處理通常點擊的不二選擇就是TouchableHighlight
。在TouchableHighlight
裏的onPress
裏調用自定義的_pressRow方法處理點擊,highlightRow
方法高亮行。
固然,這裏就少不了用到樣式了:
const styles = StyleSheet.create({ row: { flexDirection: 'row', justifyContent: 'center', padding: 10, backgroundColor: '#F6F6F6', }, text: { flex: 1, }, seperator: { height: 1, backgroundColor: '#CCCCCC' } });
在實際的開發中,通常沒有人會把Row(或者行)的繪製和ListView
放在一塊兒。咱們這裏就演示如何把Row的繪製分離出去。
首先建立一個單獨的文件,定義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也是一個組件,是一個組件就能夠在另外的組建裏渲染。因此,單獨定義的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}/> ); }
ListView在實戰中,除非是Settings之類的界面,數據都是從網絡請求獲得的。上一節中正好已經講述瞭如何使用RN內置的fetch請求網絡數據。這一節中就是用fetch來請求dribbble的數據。
在使用dribbble的數據以前你須要註冊,得到Access Token。這是請求認證所必須的。
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, }); }); }
仍是在類DemoList
裏,其餘可有可無的代碼先略去。要緊的地方是須要注意在constructor
裏設置state的時候dataSource
如何設置的。
state的改變會影響到組件的繪製。因此,在_getShots
方法裏,開始請求以前先設置一個默認的state狀態。在請求成功以後使用setState
設置一個,在catch到異常的時候再顯示另一個。
在state
裏還有一個屬性叫作isLoading: false,
。這個是影控制Loading視圖的。在false的時候隱藏,在true的時候顯示。
那麼loading界面是什麼樣呢?
<View style={{alignItems: 'center', justifyContent: 'center', flex: 1, backgroundColor: 'white'}}> <ActivityIndicator animating={true} style={[styles.centering]} size="large" color="#cccccc" /> </View>
在類DemoList
裏組合相關代碼:
_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的時候,先檢查state.isLoading
,若是須要loading視圖,那麼返回loading視圖,其餘的不返回。數據加載成功以後state.isLoading
被設置爲false,那麼顯示ListView。
以上就是處理ListView和其中的Cell的一些常見問題的方法。