- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) > web開(kāi)發(fā) >
- React Native如何自定義下拉刷新上拉加載的列表
小編給大家分享一下React Native如何自定義下拉刷新上拉加載的列表,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
在移動(dòng)端開(kāi)發(fā)中列表頁(yè)是非常常見(jiàn)的頁(yè)面,在React Native中我們一般使用FlatList或SectionList組件實(shí)現這些列表視圖。通常列表頁(yè)都會(huì )有大量的數據需要加載顯示,這時(shí)候就用到了分頁(yè)加載,因此對于列表組件來(lái)說(shuō),實(shí)現下拉刷新和上拉加載在很多情況下是必不可少的。
本篇文章基于FlatList封裝一個(gè)支持下拉刷新和上拉加載的RefreshListView,對原始的FlatList進(jìn)行封裝之后,再調用上拉和下拉刷新就十分方便了。
下拉刷新的實(shí)現十分簡(jiǎn)單,這里我們沿用FlatList本身的屬性來(lái)實(shí)現
onRefresh— 設置此選項后,則會(huì )在列表頭部添加一個(gè)標準的RefreshControl控件,以便實(shí)現“下拉刷新”的功能。同時(shí)你需要正確設置refreshing屬性。
refreshing—— bool值,用來(lái)控制刷新控件的顯示與隱藏。刷新完成后設為false。
通過(guò)這兩個(gè)屬性設置我們就可以實(shí)現FlatList頭部的刷新操作,控件使用默認的樣式,Android和iOS沿用各自系統的組件來(lái)顯示。
重點(diǎn)在于上拉加載更多,React Native的列表組件中沒(méi)有這個(gè)功能,需要我們自己實(shí)現。 對于上拉加載,通常我們有幾種狀態(tài),這里我創(chuàng )建一個(gè)RefreshState.js文件存放上拉加載的狀態(tài):
export default { Idle: 'Idle', // 初始狀態(tài),無(wú)刷新的情況 CanLoadMore: 'CanLoadMore', // 可以加載更多,表示列表還有數據可以繼續加載 Refreshing: 'Refreshing', // 正在刷新中 NoMoreData: 'NoMoreData', // 沒(méi)有更多數據了 Failure: 'Failure' // 刷新失敗 }
然后根據這幾種狀態(tài)來(lái)封裝一個(gè)RefreshFooter組件,使其根據不同狀態(tài)顯示不同內容,廢話(huà)不多說(shuō)上代碼:
import React, {Component} from 'react'; import {View, Text, ActivityIndicator, StyleSheet, TouchableOpacity} from 'react-native'; import RefreshState from './RefreshState'; import PropTypes from 'prop-types'; export default class RefreshFooter extends Component { static propTypes = { onLoadMore: PropTypes.func, // 加載更多數據的方法 onRetryLoading: PropTypes.func, // 重新加載的方法 }; static defaultProps = { footerRefreshingText: "努力加載中", footerLoadMoreText: "上拉加載更多", footerFailureText: "點(diǎn)擊重新加載", footerNoMoreDataText: "已全部加載完畢" }; render() { let {state} = this.props; let footer = null; switch (state) { case RefreshState.Idle: // Idle情況下為null,不顯示尾部組件 break; case RefreshState.Refreshing: // 顯示一個(gè)loading視圖 footer = <View style={styles.loadingView}> <ActivityIndicator size="small"/> <Text style={styles.refreshingText}>{this.props.footerRefreshingText}</Text> </View>; break; case RefreshState.CanLoadMore: // 顯示上拉加載更多的文字 footer = <View style={styles.loadingView}> <Text style={styles.footerText}>{this.props.footerLoadMoreText}</Text> </View>; break; case RefreshState.NoMoreData: // 顯示沒(méi)有更多數據的文字,內容可以自己修改 footer = <View style={styles.loadingView}> <Text style={styles.footerText}>{this.props.footerNoMoreDataText}</Text> </View>; break; case RefreshState.Failure: // 加載失敗的情況使用TouchableOpacity做一個(gè)可點(diǎn)擊的組件,外部調用onRetryLoading重新加載數據 footer = <TouchableOpacity style={styles.loadingView} onPress={()=>{ this.props.onRetryLoading && this.props.onRetryLoading(); }}> <Text style={styles.footerText}>{this.props.footerFailureText}</Text> </TouchableOpacity>; break; } return footer; } } const styles = StyleSheet.create({ loadingView: { flexDirection: 'row', justifyContent: 'center', alignItems: 'center', padding: 15, }, refreshingText: { fontSize: 12, color: "#666666", paddingLeft: 10, }, footerText: { fontSize: 12, color: "#666666" } });
注意,propTypes是我們給RefreshFooter組件定義的給外部調用的方法,方法類(lèi)型需要使用PropTypes來(lái)指定,需要安裝facebook的prop-types依賴(lài)庫,最好使用 yarn add prop-types 安裝,不容易出錯。這里用作運行時(shí)的類(lèi)型檢查,可以點(diǎn)擊這里 詳細了解。
defaultProps中我們定義了幾種不同狀態(tài)下默認的文本內容,可以在外部傳值進(jìn)行修改。
接下來(lái)就要來(lái)實(shí)現這個(gè)RefreshListView了。首先應該明確的是,這個(gè)RefreshListView要有頭部刷新和尾部刷新的調用方法,具體調用數據的方法應該在外部實(shí)現。先跟RefreshFooter一樣定義兩個(gè)方法:
static propTypes = { onHeaderRefresh: PropTypes.func, // 下拉刷新的方法,供外部調用 onFooterRefresh: PropTypes.func, // 上拉加載的方法,供外部調用 };
上面說(shuō)到頭部的下拉刷新使用FlatList自帶特性實(shí)現,我們需要定義一個(gè)bool值isHeaderRefreshing來(lái)作為refreshing屬性的值,控制頭部顯示與否。同時(shí)定義一個(gè)isFooterRefreshing來(lái)判斷尾部組件的刷新?tīng)顟B(tài)。定義footerState用來(lái)設定當前尾部組件的state,作為RefreshFooter的值。
constructor(props) { super(props); this.state = { isHeaderRefreshing: false, // 頭部是否正在刷新 isFooterRefreshing: false, // 尾部是否正在刷新 footerState: RefreshState.Idle, // 尾部當前的狀態(tài),默認為Idle,不顯示控件 } }
render函數如下:
render() { return ( <FlatList {...this.props} onRefresh={()=>{ this.beginHeaderRefresh() }} refreshing={this.state.isHeaderRefreshing} onEndReached={() => { this.beginFooterRefresh() }} onEndReachedThreshold={0.1} // 這里取值0.1(0~1之間不包括0和1),可以根據實(shí)際情況調整,取值盡量小 ListFooterComponent={this._renderFooter} /> ) } _renderFooter = () => { return ( <RefreshFooter state={this.state.footerState} onRetryLoading={()=>{ this.beginFooterRefresh() }} /> ) };
可以看到上面的代碼中有beginHeaderRefresh和beginFooterRefresh兩個(gè)方法,這兩個(gè)方法就是用來(lái)調用刷新的,但是在刷新之前還有一些邏輯情況需要判斷。比如頭部和尾部不能夠同時(shí)刷新,不然數據處理結果可能受到影響,正在刷新時(shí)要防止重復的刷新操作,這些都是要考慮的。這里我在代碼中詳細注釋了:
/// 開(kāi)始下拉刷新 beginHeaderRefresh() { if (this.shouldStartHeaderRefreshing()) { this.startHeaderRefreshing(); } } /// 開(kāi)始上拉加載更多 beginFooterRefresh() { if (this.shouldStartFooterRefreshing()) { this.startFooterRefreshing(); } } /*** * 當前是否可以進(jìn)行下拉刷新 * @returns {boolean} * * 如果列表尾部正在執行上拉加載,就返回false * 如果列表頭部已經(jīng)在刷新中了,就返回false */ shouldStartHeaderRefreshing() { if (this.state.footerState === RefreshState.refreshing || this.state.isHeaderRefreshing || this.state.isFooterRefreshing) { return false; } return true; } /*** * 當前是否可以進(jìn)行上拉加載更多 * @returns {boolean} * * 如果底部已經(jīng)在刷新,返回false * 如果底部狀態(tài)是沒(méi)有更多數據了,返回false * 如果頭部在刷新,則返回false * 如果列表數據為空,則返回false(初始狀態(tài)下列表是空的,這時(shí)候肯定不需要上拉加載更多,而應該執行下拉刷新) */ shouldStartFooterRefreshing() { if (this.state.footerState === RefreshState.refreshing || this.state.footerState === RefreshState.NoMoreData || this.props.data.length === 0 || this.state.isHeaderRefreshing || this.state.isFooterRefreshing) { return false; } return true; }
其中startHeaderRefreshing和startFooterRefreshing的邏輯如下:
/// 下拉刷新,設置完刷新?tīng)顟B(tài)后再調用刷新方法,使頁(yè)面上可以顯示出加載中的UI,注意這里setState寫(xiě)法 startHeaderRefreshing() { this.setState( { isHeaderRefreshing: true }, () => { this.props.onHeaderRefresh && this.props.onHeaderRefresh(); } ); } /// 上拉加載更多,將底部刷新?tīng)顟B(tài)改為正在刷新,然后調用刷新方法,頁(yè)面上可以顯示出加載中的UI,注意這里setState寫(xiě)法 startFooterRefreshing() { this.setState( { footerState: RefreshState.Refreshing, isFooterRefreshing: true }, () => { this.props.onFooterRefresh && this.props.onFooterRefresh(); } ); }
在刷新之前,我們需要將頭部或尾部的組件顯示出來(lái),然后再調用外部的數據接口方法。這里setState這樣寫(xiě)的好處是state中的值更新完成后才會(huì )調用箭頭函數中的方法,是有嚴格順序的,如果把 this.props.onFooterRefresh && this.props.onFooterRefresh() 寫(xiě)在setState外部,在UI上我們可能看不到頭部的loading或者尾部的努力加載中,接口方法就已經(jīng)調用完畢了。
最后,在刷新結束后我們還需要調用停止刷新的方法,使頭部或尾部組件不再顯示,否則一直是加載中還可能讓人以為是bug。下面看看停止刷新的方法:
/** * 根據尾部組件狀態(tài)來(lái)停止刷新 * @param footerState * * 如果刷新完成,當前列表數據源是空的,就不顯示尾部組件了。 * 這里這樣做是因為通常列表無(wú)數據時(shí),我們會(huì )顯示一個(gè)空白頁(yè),如果再顯示尾部組件如"沒(méi)有更多數據了"就顯得很多余 */ endRefreshing(footerState: RefreshState) { let footerRefreshState = footerState; if (this.props.data.length === 0) { footerRefreshState = RefreshState.Idle; } this.setState({ footerState: footerRefreshState, isHeaderRefreshing: false, isFooterRefreshing: false }) }
這里傳入一個(gè)尾部組件狀態(tài)的參數是為了更新尾部組件的樣式。同時(shí)對數據源data進(jìn)行一個(gè)判斷,如果為空說(shuō)明當前沒(méi)有數據,可以顯示空白頁(yè)面,那么尾部組件也沒(méi)必要顯示了。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系站長(cháng)郵箱:ts@56dr.com進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2 建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站