React Native之組件FlatList

FlatList是一個(gè)高性能的列表組件膳犹,它是ListView組件的升級(jí)版可柿,性能方面有了很大的提升役听,當(dāng)然也就建議大家在實(shí)現(xiàn)列表功能時(shí)使用FlatList,盡量不要使用ListView续扔,更不要使用ScrollView离唬。既然說到FlatList子眶,那就先溫習(xí)一下它支持的功能吓懈。高性能的簡單列表組件蝙眶,支持下面這些常用的功能:

完全跨平臺(tái)。
支持水平布局模式恨樟。
行組件顯示或隱藏時(shí)可配置回調(diào)事件半醉。
支持單獨(dú)的頭部組件。
支持單獨(dú)的尾部組件劝术。
支持自定義行間分隔線缩多。
支持下拉刷新。
支持上拉加載养晋。
支持跳轉(zhuǎn)到指定行(ScrollToIndex)

一衬吆、FlatList的使用方法

最簡單的例子:


this.state = {
error: false,
page: 1,
data:[],
refreshing: false,
loading: false,
};
}

requestData = () => {
const url = 'https://api.github.com/users/futurechallenger/repos';
fetch(url).then(res => {
console.log('started fetch');
return res.json()
}).then(res => {
this.setState({
data: [...this.state.data, ...res],
error: res.error || null,
laoding: false,
refreshing: false,
});
}).catch(err => {
console.log('==> fetch error', err);
this.setState({ error: err, loading: false, refreshing: false});
});
};

handleRefresh = () => {
this.setState({
page: 1,
refreshing: true,
loading: false,
data: [],
}, () => {
this.requestData();
});
};

handleLoadMore = () => {
this.setState({
page: this.state.page + 1,
}, () => {
this.requestData();
});
};

renderItem = (item) => (
<MessageCell item={item} />
)

render() {
return (
<View style={styles.container}>
<Text>Message</Text>
<FlatList
data={this.state.data || []}
renderItem={this.renderItem}
keyExtractor={item => item.id}
refreshing={this.state.refreshing}
onRefresh={this.handleRefresh}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0} />
</View>
);
}
}


二、FlatList常用的屬性

ItemSeparatorComponent?: ?ReactClass<any>

行與行之間的分隔線組件绳泉。 不會(huì)出現(xiàn)在第一行之前和最后一行之后逊抡。

ListEmptyComponent?: ?ReactClass<any> | React.Element<any>

列表為空時(shí)渲染該組件。 可以是React Component, 也可以是一個(gè)render函數(shù)零酪, 或者渲染好的element秦忿。

ListFooterComponent?: ?ReactClass<any>

尾部組件

ListHeaderComponent?: ?ReactClass<any>

頭部組件
columnWrapperStyle?: StyleObj

如果設(shè)置了多列布局(即將numColumns值設(shè)為大于1的整數(shù))麦射,則可以額外指定此樣式作用在每行容器上。

data: ?Array<ItemT>

為了簡化起見灯谣,data屬性目前只支持普通數(shù)組。 如果需要使用其他特殊數(shù)據(jù)結(jié)構(gòu)蛔琅,例如immutable數(shù)組胎许,請(qǐng)直接使用更底層的VirtualizedList組件。

extraData?: any

如果有除data以外的數(shù)據(jù)用在列表中(不論是用在renderItem還是Header或者Footer中)罗售,請(qǐng)?jiān)诖藢傩灾兄付ā?同時(shí)此數(shù)據(jù)在修改時(shí)也需要先修改其引用地址(比如先復(fù)制到一個(gè)新的Object或者數(shù)組中)辜窑,然后再修改其值,否則界面很可能不會(huì)刷新寨躁。

getItem?:

getItemCount?:

getItemLayout?: (data: ?Array<ItemT>, index: number) => {length: number, offset: number, index: number}

getItemLayout是一個(gè)可選的優(yōu)化穆碎,用于避免動(dòng)態(tài)測量內(nèi)容尺寸的開銷,不過前提是你可以提前知道內(nèi)容的高度职恳。 如果你的行高是固定的所禀,getItemLayout用起來就既高效又簡單,類似下面這樣:

getItemLayout={(data, index) => ( {length: 行高, offset: 行高 * index, index} )}
注意如果你指定了SeparatorComponent放钦,請(qǐng)把分隔線的尺寸也考慮到offset的計(jì)算之中色徘。

horizontal?: ?boolean

設(shè)置為true則變?yōu)樗讲季帜J健?/p>

initialNumToRender: number

指定一開始渲染的元素?cái)?shù)量,最好剛剛夠填滿一個(gè)屏幕操禀,這樣保證了用最短的時(shí)間給用戶呈現(xiàn)可見的內(nèi)容褂策。 注意這第一批次渲染的元素不會(huì)在滑動(dòng)過程中被卸載,這樣是為了保證用戶執(zhí)行返回頂部的操作時(shí)颓屑,不需要重新渲染首批元素斤寂。

initialScrollIndex?: ?number

Instead of starting at the top with the first item, start at initialScrollIndex. This disables the "scroll to top" optimization that keeps the first initialNumToRender items always rendered and immediately renders the items starting at this initial index. Requires getItemLayout to be implemented.

keyExtractor: (item: ItemT, index: number) => string

此函數(shù)用于為給定的item生成一個(gè)不重復(fù)的key。 Key的作用是使React能夠區(qū)分同類元素的不同個(gè)體揪惦,以便在刷新時(shí)能夠確定其變化的位置遍搞,減少重新渲染的開銷。 若不指定此函數(shù)丹擎,則默認(rèn)抽取item.key作為key值尾抑。 若item.key也不存在,則使用數(shù)組下標(biāo)蒂培。

legacyImplementation?: ?boolean

設(shè)置為true則使用舊的ListView的實(shí)現(xiàn)再愈。

numColumns: number

多列布局只能在非水平模式下使用,即必須是horizontal={false}护戳。 此時(shí)組件內(nèi)元素會(huì)從左到右從上到下按Z字形排列翎冲,類似啟用了flexWrap的布局。 組件內(nèi)元素必須是等高的——暫時(shí)還無法支持瀑布流布局媳荒。

onEndReached?: ?(info: {distanceFromEnd: number}) => void

當(dāng)列表被滾動(dòng)到距離內(nèi)容最底部不足onEndReachedThreshold的距離時(shí)調(diào)用抗悍。

onEndReachedThreshold?: ?number

決定當(dāng)距離內(nèi)容最底部還有多遠(yuǎn)時(shí)觸發(fā)onEndReached回調(diào)驹饺。 注意此參數(shù)是一個(gè)比值而非像素單位。 比如缴渊,0.5表示距離內(nèi)容最底部的距離為當(dāng)前列表可見長度的一半時(shí)觸發(fā)赏壹。

onRefresh?: ?() => void

如果設(shè)置了此選項(xiàng),則會(huì)在列表頭部添加一個(gè)標(biāo)準(zhǔn)的RefreshControl控件衔沼,以便實(shí)現(xiàn)“下拉刷新”的功能蝌借。 同時(shí)你需要正確設(shè)置refreshing屬性。

onViewableItemsChanged?: ?(info: {viewableItems: Array<ViewToken>, changed: Array<ViewToken>}) => void

在可見行元素變化時(shí)調(diào)用指蚁。 可見范圍和變化頻率等參數(shù)的配置請(qǐng)?jiān)O(shè)置viewabilityconfig屬性

refreshing?: ?boolean

在等待加載新數(shù)據(jù)時(shí)將此屬性設(shè)為true菩佑,列表就會(huì)顯示出一個(gè)正在加載的符號(hào)。

renderItem: (info: {item: ItemT, index: number}) => ?React.Element<any>

根據(jù)行數(shù)據(jù)data渲染每一行的組件凝化。 典型用法:

<FlatList ItemSeparatorComponent={Platform.OS !== 'android' && ({highlighted}) => ( <View style={[style.separator, highlighted && {marginLeft: 0}]} /> )} data={[{title: 'Title Text', key: 'item1'}]} renderItem={({item, separators}) => ( <TouchableHighlight onPress={() => this._onPress(item)} onShowUnderlay={separators.highlight} onHideUnderlay={separators.unhighlight}> <View style={{backgroundColor: 'white'}}> <Text>{item.title}}</Text> </View> </TouchableHighlight> )} />
Provides additional metadata like index if you need it, as well as a more generic separators.updateProps function which let's you set whatever props you want to change the rendering of either the leading separator or trailing sep arator in case the more common highlight and unhighlight (which set the highlighted: boolean prop) are insufficient for your use-case.

viewabilityConfig?: ViewabilityConfig #

請(qǐng)參考ViewabilityHelper的源碼來了解具體的配置稍坯。

三、ListView常用的方法

scrollToEnd(params?: object)

滾動(dòng)到底部搓劫。 如果不設(shè)置getItemLayout屬性的話瞧哟,可能會(huì)比較卡。

scrollToIndex(params: object)

Scrolls to the item at a the specified index such that it is positioned in the viewable area such that viewPosition 0 pla ces it at the top, 1 at the bottom, and 0.5 centered in the middle.

如果不設(shè)置getItemLayout屬性的話糟把,無法跳轉(zhuǎn)到當(dāng)前可視區(qū)域以外的位置绢涡。

scrollToItem(params: object)

Requires linear scan through data - use scrollToIndex instead if possible. 如果不設(shè)置getItemLayout屬性的話,可能會(huì)比較卡遣疯。

scrollToOffset(params: object)

Scroll to a specific content pixel offset, like a normal ScrollView.

recordInteraction()

Tells the list an interaction has occured, which should trigger viewability calculations, e.g. if waitForInteractions is true and the user has not scrolled. This is typically called by taps on items or by navigation actions.

封裝好的上拉下拉加載更多Demo


import React, {PureComponent} from 'react'
import {View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, Dimensions} from 'react-native'

export const RefreshState = {
Idle: 0,
HeaderRefreshing: 1,
FooterRefreshing: 2,
NoMoreData: 3,
Failure: 4,
}

const DEBUG = false
const log = (text: string) => {DEBUG && console.log(text)}

const footerRefreshingText = 'Job Loading…'
const footerFailureText = 'Click Reload'
const footerNoMoreDataText = ''

type Props = {
refreshState: number,
onHeaderRefresh: (refreshState: number) => void,
onFooterRefresh?: (refreshState: number) => void,
data: Array<any>,

footerContainerStyle?: any,
footerTextStyle?: any,
}

type State = {}

class RefreshListView extends PureComponent {
props: Props
state: State

componentWillReceiveProps(nextProps: Props) {
log('[RefreshListView] RefreshListView componentWillReceiveProps ' + nextProps.refreshState)
}

componentDidUpdate(prevProps: Props, prevState: State) {
log('[RefreshListView] RefreshListView componentDidUpdate ' + prevProps.refreshState)
}

onHeaderRefresh = () => {
log('[RefreshListView] onHeaderRefresh')

if (this.shouldStartHeaderRefreshing()) {
log('[RefreshListView] onHeaderRefresh')
this.props.onHeaderRefresh(RefreshState.HeaderRefreshing)
}
}

onEndReached = (info: any) => {
log('[RefreshListView] onEndReached ' + info.distanceFromEnd)

if (this.shouldStartFooterRefreshing()) {
log('[RefreshListView] onFooterRefresh')
this.props.onFooterRefresh && this.props.onFooterRefresh(RefreshState.FooterRefreshing)
}
}

shouldStartHeaderRefreshing = () => {
log('[RefreshListView] shouldStartHeaderRefreshing')

if (this.props.refreshState == RefreshState.HeaderRefreshing ||
this.props.refreshState == RefreshState.FooterRefreshing) {
return false
}

return true
}

shouldStartFooterRefreshing = () => {
log('[RefreshListView] shouldStartFooterRefreshing')

let {refreshState, data} = this.props
if (data.length == 0) {
return false
}

return (refreshState == RefreshState.Idle)
}

render() {
log('[RefreshListView] render')

return (
<FlatList
onEndReached={this.onEndReached}
onRefresh={this.onHeaderRefresh}
refreshing={this.props.refreshState == RefreshState.HeaderRefreshing}
ListFooterComponent={this.renderFooter}
{...this.props}
/>
)
}

renderFooter = () => {
let footer = null

let footerContainerStyle = [styles.footerContainer, this.props.footerContainerStyle]
let footerTextStyle = [styles.footerText, this.props.footerTextStyle]
switch (this.props.refreshState) {
case RefreshState.Idle:
footer = (<View style={footerContainerStyle} />)
break
case RefreshState.Failure: {
footer = (
<TouchableOpacity
style={footerContainerStyle}
onPress={() => {
this.props.onFooterRefresh && this.props.onFooterRefresh(RefreshState.FooterRefreshing)
}}
>
<Text style={footerTextStyle}>{footerFailureText}</Text>
</TouchableOpacity>
)
break
}
case RefreshState.FooterRefreshing: {
footer = (
<View style={footerContainerStyle} >
<ActivityIndicator size="small" color="#888888" />
<Text style={[footerTextStyle, {marginLeft: 7}]}>{footerRefreshingText}</Text>
</View>
)
break
}
case RefreshState.NoMoreData: {
footer = (
<View style={footerContainerStyle} >
<Text style={footerTextStyle}>{footerNoMoreDataText}</Text>
</View>
)
break
}
}

return footer
}
}

const styles = StyleSheet.create({
footerContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
padding: 10,
height: 44,
},
footerText: {
fontSize: 14,
color: '#555555'
}
})

export default RefreshListView

調(diào)用

<RefreshListView
data={this.state.dataSource}
keyExtractor={item => item.id}
renderItem={({item}) => this.renderItem(item)}
refreshState={this.state.refreshState}
onHeaderRefresh={this.onHeaderRefresh}
onFooterRefresh={this.onFooterRefresh}
onEndReachedThreshold={0.1}
/>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末雄可,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子缠犀,更是在濱河造成了極大的恐慌数苫,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辨液,死亡現(xiàn)場離奇詭異虐急,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)滔迈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門止吁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人燎悍,你說我怎么就攤上這事敬惦。” “怎么了谈山?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵俄删,是天一觀的道長。 經(jīng)常有香客問我,道長畴椰,這世上最難降的妖魔是什么臊诊? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮斜脂,結(jié)果婚禮上抓艳,老公的妹妹穿的比我還像新娘。我一直安慰自己秽褒,他們只是感情好壶硅,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著销斟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪椒舵。 梳的紋絲不亂的頭發(fā)上蚂踊,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音笔宿,去河邊找鬼犁钟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛泼橘,可吹牛的內(nèi)容都是我干的涝动。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼炬灭,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼醋粟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起重归,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤米愿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后鼻吮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體育苟,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年椎木,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了违柏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡香椎,死狀恐怖漱竖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情士鸥,我是刑警寧澤闲孤,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響讼积,放射性物質(zhì)發(fā)生泄漏肥照。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一勤众、第九天 我趴在偏房一處隱蔽的房頂上張望舆绎。 院中可真熱鬧,春花似錦们颜、人聲如沸吕朵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽努溃。三九已至,卻和暖如春阻问,著一層夾襖步出監(jiān)牢的瞬間梧税,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工称近, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留第队,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓刨秆,卻偏偏與公主長得像凳谦,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子衡未,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容尸执,還有我對(duì)于 Vue 1.0 印象不深的內(nèi)容。關(guān)于...
    云之外閱讀 5,046評(píng)論 0 29
  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案眠屎? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 13,737評(píng)論 1 92
  • 不知道自己怎么讓原本手上一個(gè)小膿包長成了一個(gè)肉瘤剔交,必須清除,因?yàn)樘弁措y忍改衩。 于是一個(gè)人經(jīng)過了五次到校醫(yī)院后岖常,終于有...
    照亮Br閱讀 281評(píng)論 5 0
  • 本名 李煜 別稱 南唐后主,李后主葫督,詞帝 字號(hào) 字重光 號(hào)鐘隱竭鞍、蓮峰居士 所處時(shí)代 五代十國...
    額呵額呵額閱讀 283評(píng)論 0 0
  • 在成都工作的第三天,爸爸收到了來自男盆友爸爸的投訴橄镜。他爸爸似乎十分介意我不愿意在他們家里住的事偎快,我也不是很明白,為...
    _lexi_閱讀 78評(píng)論 0 0