React Native入門(七)之列表元件的使用(2)關於FlatList的一切
前言
在上一篇部落格中瞭解了列表元件的相關內容,主要是靜態的展示了一個列表資料,瞭解了ScrollVIew,FlatList和SectionList的基本用法,本篇文章就深入的瞭解一個常用的一個列表元件FlatList的用法!
屬性
新增頭部元件
ListHeaderComponent
屬性用來給FlatList新增頭部元件
簡單使用:
//ES6之前寫法
_header = function () {
return (
<Text style={{fontWeight: 'bold', fontSize: 20}}>熱門電影</Text>
);
}
<FlatList
ListHeaderComponent={this ._header}//header頭部元件
/>
新增尾部元件
ListFooterComponent
屬性為FlatList新增尾部元件,接收的引數跟ListHeaderComponent
相同。
//ES6的寫法
_footer = () => (
<Text style={{fontSize: 14, alignSelf: 'center'}}>到底啦,沒有啦!</Text>
)
<FlatList
ListFooterComponent={this._footer} //新增尾部元件
/>
新增頭部和尾部元件比較簡單,需要注意的就是上邊兩者ES5和6寫法的區別!
新增分割線
ItemSeparatorComponent
屬性可以為FlatList列表之間新增分割線。
舉個例子:
class ItemDivideComponent extends Component {
render() {
return (
<View style={{height: 1, backgroundColor: 'skyblue'}}/>
);
}
};
<FlatList
ItemSeparatorComponent={ItemDivideComponent}//分割線元件
/>
這裡我們自定義了一個元件來設定分割線,當然我們像新增頭部和尾部一樣,在內部宣告之後使用this._header
設定空資料檢視
ListEmptyComponent
屬性,可以為FlatList設定一個沒有資料的時候展示的檢視!,這個屬性可以接收的引數型別比較多,可以是React Component
,也可以是一個render函式
,或者渲染好的element
。
所以設定空資料檢視不僅可以像前邊介紹的兩種方式以外,還可以接收一個render函式
。
舉個例子:
createEmptyView() {
return (
<Text style={{fontSize: 40, alignSelf: 'center'}}>還沒有資料哦!</Text>
);
}
<FlatList
ListEmptyComponent={this.createEmptyView()}
/>
設定item的key
在前一篇部落格中,我們的設定data
的時候,是這樣的:
data={[
{key: '大護法'},
{key: '繡春刀II:修羅戰場'},
...
]}
類似{key:你的資料value}
這樣的形式,是因為我們在設定data
的時候,必須要為item設定key
屬性,否則會有一個黃色的警告彈出。而且我們需要注意的是這裡每一個item的key
是唯一的!,如果按照這樣的寫法,我們在資料中有重複的,比如{key: '大護法'},{key: '大護法'}
,這裡的大護法
只會顯示一個,因為FlatList會認為這是一條資料,因為key
相同!
那麼為什麼會這樣?
因為FlatList中有一個屬性:keyExtractor
,用於為給定的item生成一個不重複的key。若不指定此函式,則預設抽取item.key作為key值。若item.key也不存在,則使用陣列下標index。因為前邊沒有指定該屬性,所以就把item.key
作為了key值,才會認定兩個重複的資料是一條資料!
那麼一般地,我們可以這樣使用:
_keyExtractor = (item, index) => index;
<FlatList
keyExtractor={this._keyExtractor}
/>
這樣就把data
中陣列的下標作為了唯一的key
。
那麼在data
中指定資料的時候,就不用{key: '大護法'}
這樣寫了,因為我們已經指定了唯一的key,而可以隨意寫{name: '大護法'}
或者{movie: '大護法'}
,在渲染item的時候,取值用item.name
或者item.movie
即可!也不會有黃色的警告出現!
你明白了嗎?
設定itemLayout
getItemLayout
屬性是一個可選的優化,用於避免動態測量內容尺寸的開銷。如果我們知道item的高度,就可以為FlatList指定這一個屬性,來使FlatList更加高效的執行!
舉個例子:
getItemLayout={(data, index) => ({
length: 44, offset: (44 + 1) * index, index
})}
我們在上邊使用的時候指定了item的高度為44,所以length
引數為44;我們設定了分割線,且指定分割線的高度是1,所以offset
引數為44+1。綜合來看,設定這個屬性應這樣寫:
getItemLayout={(data, index) => ({
length: 你的item的height, offset: (你的item的height + ItemSeparator的height) * index, index
})}
設定這一屬性,在呼叫FlatList的跳轉函式的時候非常有用,否則可能會很卡頓!如scrollToEnd()
,scrollToIndex()
,這兩個方法後邊再說!
下拉重新整理
FlatList中有兩個屬性,可以用來設定下拉重新整理。
refreshing
在等待載入新資料時將此屬性設為true,列表就會顯示出一個正在載入的符號.onRefresh
如果設定了此選項,則會在列表頭部新增一個標準的RefreshControl
控制元件,以便實現“下拉重新整理”的功能。同時你需要正確設定refreshing
屬性。
這裡的RefreshControl
控制元件,非常類似於Android v4包中的SwipeRefreshLayout
,這裡就不多說了,需要了解的可以檢視相關文件!
如何使用,舉個例子:
refreshing={this.state.refreshing}
onRefresh={() => {
this.setState({refreshing: true})//開始重新整理
//這裡模擬請求網路,拿到資料,3s後停止重新整理
setTimeout(() => {
alert('沒有可重新整理的內容!');
this.setState({refreshing: false});//停止重新整理
}, 3000);
}}
上拉載入
關於上拉載入,FlatList也封裝有兩個屬性來實現:
onEndReachedThreshold
:這個屬性決定當距離內容最底部還有多遠時觸發onEndReached回撥。需要注意的是此引數是一個比值而非畫素單位。比如,0.5表示距離內容最底部的距離為當前列表可見長度的一半時觸發。所以它的取值範圍為:(0,1),不包含0和1。onEndReached
:列表被滾動到距離內容最底部不足onEndReachedThreshold設定的的距離時呼叫。
具體使用,舉個例子:
onEndReachedThreshold={0.1}
onEndReached={({distanceFromEnd}) => (
setTimeout(() => {
this.setState((state) => ({
data: state.data.concat(this._newData),
}));
}, 3000)
)}
這裡我們設定的距離為列表可見長度的1/10,而觸發了onEndReached函式時,我們設定了一個定時器,3s後,將data
中的資料添加了新資料,從而達到上拉載入更多的效果!
2018.5.22更新:
隱藏滑動指示條
//隱藏垂直狀態條
showsVerticalScrollIndicator={false}
//如果設定為橫向
horizontal={true}
//隱藏水平狀態條
showsHorizontalScrollIndicator={false}
函式
介紹
FlatList下有兩個比較常用的函式:
scrollToEnd()
滾動到底部。如果不設定getItemLayout
屬性的話,可能會比較卡。scrollToIndex()
如果不設定getItemLayout
屬性的話,無法跳轉到當前可視區域以外的位置。
如官方所言,使用這兩個函式的時候,最好指定設定getItemLayout
屬性。
使用
因為這兩個是FlatList元件的函式,所以在使用這兩個函式之前,首先我們要得到FlatList元件的引用。
這時候就需要ref
屬性。React提供的這個ref
屬性,表示為對元件真正例項的引用。
關於ref
屬性的使用,可以去React官網檢視API!
具體的使用:
ref={(flatList) => this._flatList = flatList}
this._flatList.scrollToEnd();
//viewPosition引數:0表示頂部,0.5表示中部,1表示底部
this._flatList.scrollToIndex({viewPosition: 0, index: this.state.text});
至此,關於FlatList進階的相關基礎內容就說完了!
2017/11/7更新:
在使用橫向列表的時候,也就是設定horizontal={true}
的時候,然後新增分割線ItemSeparatorComponent,會出現問題,就是顯示位置錯誤,我提過問題,也在Github上提了一個issue,在最新的v0.50版本修復了這個問題!
FlatList when set horizontal={true}
,ItemSeparatorComponent can’t be shown correctly #16518
完整的Demo
下面看一下完整的demo:
原始碼:
class FlatListTest extends Component {
constructor(props) {
super(props);
this.state = {
data: this._sourceData,
refreshing: false, //初始化不重新整理
text: ''//跳轉的行
};
}
_header = function () {
return (
<Text style={{fontWeight: 'bold', fontSize: 20}}>熱門電影</Text>
);
}
_footer = () => (
<Text style={{fontSize: 14, alignSelf: 'center'}}>到底啦,沒有啦!</Text>
)
createEmptyView() {
return (
<Text style={{fontSize: 40, alignSelf: 'center'}}>還沒有資料哦!</Text>
);
}
//此函式用於為給定的item生成一個不重複的key
//若不指定此函式,則預設抽取item.key作為key值。若item.key也不存在,則使用陣列下標index。
_keyExtractor = (item, index) => index;
itemClick(item, index) {
alert('點選了第' + index + '項,電影名稱為:' + item.name);
}
_renderItem = ({item, index}) => {
return (
<TouchableOpacity
activeOpacity={0.5}
onPress={this.itemClick.bind(this, item, index)}>
<Text style={flatListStyles.item}>{item.name}</Text>
</TouchableOpacity>
);
}
//點選按鈕跳轉
onButtonPress() {
//viewPosition引數:0表示頂部,0.5表示中部,1表示底部
this._flatList.scrollToIndex({viewPosition: 0, index: this.state.text});
//this._flatList.scrollToOffset({ animated: true, offset: 2000 });
};
onBtnPress2Botton() {
this._flatList.scrollToEnd();
}
_sourceData = [
{name: '大護法'},
{name: '繡春刀II:修羅戰場'},
{name: '神偷奶爸3'},
{name: '神奇女俠'},
{name: '摔跤吧,爸爸'},
{name: '悟空傳'},
{name: '閃光少女'},
{name: '攻殼機動隊'},
{name: '速度與激情8'},
{name: '蝙蝠俠大戰超人'},
{name: '攻殼機動隊'},
{name: '速度與激情8'},
{name: '蝙蝠俠大戰超人'}
]
_newData = [{name: '我是新新增的資料1'},
{name: '我是新新增的資料2'},
{name: '我是新新增的資料3'}]
render() {
return (
<View style={flatListStyles.container}>
<View style={{flexDirection: 'row', justifyContent: 'center', alignItems: 'center'}}>
<TextInput
style={{flex: 1}}
placeholder="請輸入要跳轉的行號"
onChangeText={(text) => this.setState({text})}
/>
<Button title="跳轉到行" onPress={this.onButtonPress.bind(this)} color={'skyblue'}/>
<Button title="跳轉到底部" onPress={this.onBtnPress2Botton.bind(this)} color={'green'}/>
</View>
<FlatList
data={this.state.data}
//使用 ref 可以獲取到相應的元件
ref={(flatList) => this._flatList = flatList}
ListHeaderComponent={this._header}//header頭部元件
ListFooterComponent={this._footer}//footer尾部元件
ItemSeparatorComponent={ItemDivideComponent}//分割線元件
//空資料檢視,可以是React Component,也可以是一個render函式,或者渲染好的element。
ListEmptyComponent={this.createEmptyView()}
keyExtractor={this._keyExtractor}
//是一個可選的優化,用於避免動態測量內容尺寸的開銷,不過前提是你可以提前知道內容的高度。
//如果你的行高是固定的,getItemLayout用起來就既高效又簡單.
//注意如果你指定了SeparatorComponent,請把分隔線的尺寸也考慮到offset的計算之中
getItemLayout={(data, index) => ( {length: 44, offset: (44 + 1) * index, index} )}
//決定當距離內容最底部還有多遠時觸發onEndReached回撥。
//注意此引數是一個比值而非畫素單位。比如,0.5表示距離內容最底部的距離為當前列表可見長度的一半時觸發。
onEndReachedThreshold={0.1}
//當列表被滾動到距離內容最底部不足onEndReachedThreshold的距離時呼叫
onEndReached={({distanceFromEnd}) => (
setTimeout(() => {
this.setState((state) => ({
data: state.data.concat(this._newData),
}));
}, 3000)
)}
refreshing={this.state.refreshing}
onRefresh={() => {
this.setState({refreshing: true})//開始重新整理
//這裡模擬請求網路,拿到資料,3s後停止重新整理
setTimeout(() => {
alert('沒有可重新整理的內容!');
this.setState({refreshing: false});
}, 3000);
}}
renderItem={this._renderItem}
/>
</View>
);
}
}
;
class ItemDivideComponent
extends Component {
render() {
return (
<View style={{height: 1, backgroundColor: 'skyblue'}}/>
);
}
}
;
const flatListStyles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 22
},
item: {
padding: 10,
fontSize: 18,
height: 44,
},
})
AppRegistry.registerComponent('AwesomeProject', () => FlatListTest);
結語
好了,關於FlatList基本的進階內容都在這裡了,我也是初學者,如果有什麼錯誤或者疑問,還請不吝賜教!
我們下一篇再見!