React Native 自定义下拉刷新上拉加载的列表的示例

网友投稿 746 2023-02-15


React Native 自定义下拉刷新上拉加载的列表的示例

在移动端开发中列表页是非常常见的页面,在React Native中我们一般使用FlatList或SectionList组件实现这些列表视图。通常列表页都会有大量的数据需要加载显示,这时候就用到了分页加载,因此对于列表组件来说,实现下拉刷新和上拉加载在很多情况下是必不可少的。

本篇文章基于FlatList封装一个支持下拉刷新和上拉加载的RefreshListView,对原始的FlatList进行封装之后,再调用上拉和下拉刷新就十分方便了。

下拉刷新的实现十分简单,这里我们沿用FlatList本身的属性来实现

onRefresh— 设置此选项后,则会在列表头部添加一个标准的RefreshControl控件,以便实现“下拉刷新”的功能。同时你需要正确设置refreshing属性。

refreshing—— bool值,用来控制刷新控件的显示与隐藏。刷新完成后设为false。

通过这两个属性设置我们就可以实现FlatList头部的刷新操作,控件使用默认的样式,android和iOS沿用各自系统的组件来显示。

重点在于上拉加载更多,React Native的列表组件中没有这个功能,需要我们自己实现。 对于上拉加载,通常我们有几种状态,这里我创建一个RefreshState.js文件存放上拉加载的状态:

export default {

Idle: 'Idle', // 初始状态,无刷新的情况

CanLoadMore: 'CanLoadMore', // 可以加载更多,表示列表还有数据可以继续加载

Refreshing: 'Refreshing', // 正在刷新中

NoMoreData: 'NoMoreData', // 没有更多数据了

Failure: 'Failure' // 刷新失败

}

然后根据这几种状态来封装一个RefreshFooter组件,使其根据不同状态显示不同内容,废话不多说上代码:

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: "点击重新加载",

footerNoMoreDataText: "已全部加载完毕"

http:// };

render() {

let {state} = this.props;

let footer = null;

switch (state) {

case RefreshState.Idle:

// Idle情况下为null,不显示尾部组件

break;

case RefreshState.Refreshing:

// 显示一个loading视图

footer =

{this.props.footerRefreshingText}

;

break;

case RefreshState.CanLoadMore:

// 显示上拉加载更多的文字

footer =

{this.props.footerLoadMoreText}

;

break;

case RefreshState.NoMoreData:

// 显示没有更多数据的文字,内容可以自己修改

footer =

{this.props.footerNoMoreDataText}

;

break;

case RefreshState.Failure:

// 加载失败的情况使用TouchableOpacity做一个可点击的组件,外部调用onRetryLoading重新加载数据

footer =

{

this.props.onRetryLoading && this.props.onRetryLoading();

}}>

{this.props.footerFailureText}

;

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组件定义的给外部调用的方法,方法类型需要使用PropTypes来指定,需要安装facebook的prop-types依赖库,最好使用 yarn add prop-types 安装,不容易出错。这里用作运行时的类型检查,可以点击这里 详细了解。

defaultProps中我们定义了几种不同状态下默认的文本内容,可以在外部传值进行修改。

接下来就要来实现这个RefreshListView了。首先应该明确的是,这个RefreshListView要有头部刷新和尾部刷新的调用方法,具体调用数据的方法应该在外部实现。先跟RefreshFooter一样定义两个方法:

static propTypes = {

onHeaderRefresh: PropTypes.func, // 下拉刷新的方法,供外部调用

onFooterRefresh: PropTypes.func, // 上拉加载的方法,供外部调用

};

上面说到头部的下拉刷新使用FlatList自带特性实现,我们需要定义一个bool值isHeaderRefreshing来作为refreshing属性的值,控制头部显示与否。同时定义一个isFooterRefreshing来判断尾部组件的刷新状态。定义footerState用来设定当前尾部组件的state,作为RefreshFooter的值。

constructor(props) {

super(props);

this.state = {

isHeaderRefreshing: false, // 头部是否正在刷新

isFooterRefreshing: false, // 尾部是否正在刷新

footerState: RefreshState.Idle, // 尾部当前的状态,默认为Idle,不显示控件

}

}

render函数如下:

render() {

return (

{...this.props}

onRefresh={()=>{ this.beginHeaderRefresh() }}

refreshing={this.state.isHeaderRefreshing}

onEndReached={() => { this.beginFooterRefresh() }}

onEndReachedThreshold={0.1} // 这里取值0.1(0~1之间不包括0和1),可以根据实际情况调整,取值尽量小

ListFooterComponent={this._renderFooter}

/>

)

}

_renderFooter = () => {

return (

state={this.state.footerState}

onRetryLoading={()=>{

this.beginFooterRefresh()

}}

/>

)

};

可以看到上面的代码中有beginHeaderRefresh和beginFooterRefresh两个方法,这两个方法就是用来调用刷新的,但是在刷新之前还有一些逻辑情况需要判断。比如头部和尾部不能够同时刷新,不然数据处理结果可能受到影响,正在刷新时要防止重复的刷新操作,这些都是要考虑的。这里我在代码中详细注释了:

/// 开始下拉刷新

beginHeaderRefresh() {

if (this.shouldStartHeaderRefreshing()) {

this.startHeaderRefreshing();

}

}

/// 开始上拉加载更多

beginFooterRefresh() {

if (this.shouldStartFooterRefreshing()) {

this.startFooterRefreshing();

}

}

/***

* 当前是否可以进行下拉刷新

* @returns {boolean}

*

* 如果列表尾部正在执行上拉加载,就返回false

* 如果列表头部已经在刷新中了,就返回false

*/

shouldStartHeaderRefreshing() {

if (this.state.footerState === RefreshState.refreshing ||

this.state.isHeaderRefreshing ||

this.state.isFooterRefreshing) {

return false;

}

return true;

}

/***

* 当前是否可以进行上拉加载更多

* @returns {boolean}

*

* 如果底部已经在刷新,返回false

* 如果底部状态是没有更多数据了,返回false

* 如果头部在刷新,则返回false

* 如果列表数据为空,则返回false(初始状态下列表是空的,这时候肯定不需要上拉加载更多,而应该执行下拉刷CLUuwWWidf新)

*/

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的逻辑如下:

/// 下拉刷新,设置完刷新状态后再调用刷新方法,使页面上可以显示出加载中的UI,注意这里setState写法

startHeaderRefreshing() {

this.setState(

{

isHeaderRefreshing: true

},

() => {

this.props.onHeaderRefresh && this.props.onHeaderRefresh();

}

);

}

/// 上拉加载更多,将底部刷新状态改为正在刷新,然后调用刷新方法,页面上可以显示出加载中的UI,注意这里setState写法

startFooterRefreshing() {

this.setState(

{

footerState: RefreshState.Refreshing,

isFooterRefreshing: true

},

() => {

this.props.onFooterRefresh && this.props.onFooterRefresh();

}

);

}

在刷新之前,我们需要将头部或尾部的组件显示出来,然后再调用外部的数据接口方法。这里setState这样写的好处是state中的值更新完成后才会调用箭头函数中的方法,是有严格顺序的,如果把 this.props.onFooterRefresh && this.props.onFooterRefresh() 写在setState外部,在UI上我们可能看不到头部的loading或者尾部的努力加载中,接口方法就已经调用完毕了。

最后,在刷新结束后我们还需要调用停止刷新的方法,使头部或尾部组件不再显示,否则一直是加载中还可能让人以为是bug。下面看看停止刷新的方法:

/**

* 根据尾部组件状态来停止刷新

* @param footerState

*

* 如果刷新完成,当前列表数据源是空的,就不显示尾部组件了。

* 这里这样做是因为通常列表无数据时,我们会显示一个空白页,如果再显示尾部组件如"没有更多数据了"就显得很多余

*/

endRefreshing(footerState: RefreshState) {

let footerRefreshState = footerState;

if (this.props.data.length === 0) {

footerRefreshState = RefreshState.Idle;

}

this.setState({

footerState: footerRefreshState,

isHeaderRefreshing: false,

isFooterRefreshing: false

})

}

这里传入一个尾部组件状态的参数是为了更新尾部组件的样式。同时对数据源data进行一个判断,如果为空说明当前没有数据,可以显示空白页面,那么尾部组件也没必要显示了。

以下是我使用RefreshListView实现的豆瓣电影页面分页加载的效果图:

完整的Demo地址: https://github.com/mrarronz/react-native-blog-examples/tree/master/Chapter4-PullRefresh/PullRefreshExample

{...this.props}

onRefresh={()=>{ this.beginHeaderRefresh() }}

refreshing={this.state.isHeaderRefreshing}

onEndReached={() => { this.beginFooterRefresh() }}

onEndReachedThreshold={0.1} // 这里取值0.1(0~1之间不包括0和1),可以根据实际情况调整,取值尽量小

ListFooterComponent={this._renderFooter}

/>

)

}

_renderFooter = () => {

return (

state={this.state.footerState}

onRetryLoading={()=>{

this.beginFooterRefresh()

}}

/>

)

};

可以看到上面的代码中有beginHeaderRefresh和beginFooterRefresh两个方法,这两个方法就是用来调用刷新的,但是在刷新之前还有一些逻辑情况需要判断。比如头部和尾部不能够同时刷新,不然数据处理结果可能受到影响,正在刷新时要防止重复的刷新操作,这些都是要考虑的。这里我在代码中详细注释了:

/// 开始下拉刷新

beginHeaderRefresh() {

if (this.shouldStartHeaderRefreshing()) {

this.startHeaderRefreshing();

}

}

/// 开始上拉加载更多

beginFooterRefresh() {

if (this.shouldStartFooterRefreshing()) {

this.startFooterRefreshing();

}

}

/***

* 当前是否可以进行下拉刷新

* @returns {boolean}

*

* 如果列表尾部正在执行上拉加载,就返回false

* 如果列表头部已经在刷新中了,就返回false

*/

shouldStartHeaderRefreshing() {

if (this.state.footerState === RefreshState.refreshing ||

this.state.isHeaderRefreshing ||

this.state.isFooterRefreshing) {

return false;

}

return true;

}

/***

* 当前是否可以进行上拉加载更多

* @returns {boolean}

*

* 如果底部已经在刷新,返回false

* 如果底部状态是没有更多数据了,返回false

* 如果头部在刷新,则返回false

* 如果列表数据为空,则返回false(初始状态下列表是空的,这时候肯定不需要上拉加载更多,而应该执行下拉刷CLUuwWWidf新)

*/

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的逻辑如下:

/// 下拉刷新,设置完刷新状态后再调用刷新方法,使页面上可以显示出加载中的UI,注意这里setState写法

startHeaderRefreshing() {

this.setState(

{

isHeaderRefreshing: true

},

() => {

this.props.onHeaderRefresh && this.props.onHeaderRefresh();

}

);

}

/// 上拉加载更多,将底部刷新状态改为正在刷新,然后调用刷新方法,页面上可以显示出加载中的UI,注意这里setState写法

startFooterRefreshing() {

this.setState(

{

footerState: RefreshState.Refreshing,

isFooterRefreshing: true

},

() => {

this.props.onFooterRefresh && this.props.onFooterRefresh();

}

);

}

在刷新之前,我们需要将头部或尾部的组件显示出来,然后再调用外部的数据接口方法。这里setState这样写的好处是state中的值更新完成后才会调用箭头函数中的方法,是有严格顺序的,如果把 this.props.onFooterRefresh && this.props.onFooterRefresh() 写在setState外部,在UI上我们可能看不到头部的loading或者尾部的努力加载中,接口方法就已经调用完毕了。

最后,在刷新结束后我们还需要调用停止刷新的方法,使头部或尾部组件不再显示,否则一直是加载中还可能让人以为是bug。下面看看停止刷新的方法:

/**

* 根据尾部组件状态来停止刷新

* @param footerState

*

* 如果刷新完成,当前列表数据源是空的,就不显示尾部组件了。

* 这里这样做是因为通常列表无数据时,我们会显示一个空白页,如果再显示尾部组件如"没有更多数据了"就显得很多余

*/

endRefreshing(footerState: RefreshState) {

let footerRefreshState = footerState;

if (this.props.data.length === 0) {

footerRefreshState = RefreshState.Idle;

}

this.setState({

footerState: footerRefreshState,

isHeaderRefreshing: false,

isFooterRefreshing: false

})

}

这里传入一个尾部组件状态的参数是为了更新尾部组件的样式。同时对数据源data进行一个判断,如果为空说明当前没有数据,可以显示空白页面,那么尾部组件也没必要显示了。

以下是我使用RefreshListView实现的豆瓣电影页面分页加载的效果图:

完整的Demo地址: https://github.com/mrarronz/react-native-blog-examples/tree/master/Chapter4-PullRefresh/PullRefreshExample

state={this.state.footerState}

onRetryLoading={()=>{

this.beginFooterRefresh()

}}

/>

)

};

可以看到上面的代码中有beginHeaderRefresh和beginFooterRefresh两个方法,这两个方法就是用来调用刷新的,但是在刷新之前还有一些逻辑情况需要判断。比如头部和尾部不能够同时刷新,不然数据处理结果可能受到影响,正在刷新时要防止重复的刷新操作,这些都是要考虑的。这里我在代码中详细注释了:

/// 开始下拉刷新

beginHeaderRefresh() {

if (this.shouldStartHeaderRefreshing()) {

this.startHeaderRefreshing();

}

}

/// 开始上拉加载更多

beginFooterRefresh() {

if (this.shouldStartFooterRefreshing()) {

this.startFooterRefreshing();

}

}

/***

* 当前是否可以进行下拉刷新

* @returns {boolean}

*

* 如果列表尾部正在执行上拉加载,就返回false

* 如果列表头部已经在刷新中了,就返回false

*/

shouldStartHeaderRefreshing() {

if (this.state.footerState === RefreshState.refreshing ||

this.state.isHeaderRefreshing ||

this.state.isFooterRefreshing) {

return false;

}

return true;

}

/***

* 当前是否可以进行上拉加载更多

* @returns {boolean}

*

* 如果底部已经在刷新,返回false

* 如果底部状态是没有更多数据了,返回false

* 如果头部在刷新,则返回false

* 如果列表数据为空,则返回false(初始状态下列表是空的,这时候肯定不需要上拉加载更多,而应该执行下拉刷CLUuwWWidf新)

*/

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的逻辑如下:

/// 下拉刷新,设置完刷新状态后再调用刷新方法,使页面上可以显示出加载中的UI,注意这里setState写法

startHeaderRefreshing() {

this.setState(

{

isHeaderRefreshing: true

},

() => {

this.props.onHeaderRefresh && this.props.onHeaderRefresh();

}

);

}

/// 上拉加载更多,将底部刷新状态改为正在刷新,然后调用刷新方法,页面上可以显示出加载中的UI,注意这里setState写法

startFooterRefreshing() {

this.setState(

{

footerState: RefreshState.Refreshing,

isFooterRefreshing: true

},

() => {

this.props.onFooterRefresh && this.props.onFooterRefresh();

}

);

}

在刷新之前,我们需要将头部或尾部的组件显示出来,然后再调用外部的数据接口方法。这里setState这样写的好处是state中的值更新完成后才会调用箭头函数中的方法,是有严格顺序的,如果把 this.props.onFooterRefresh && this.props.onFooterRefresh() 写在setState外部,在UI上我们可能看不到头部的loading或者尾部的努力加载中,接口方法就已经调用完毕了。

最后,在刷新结束后我们还需要调用停止刷新的方法,使头部或尾部组件不再显示,否则一直是加载中还可能让人以为是bug。下面看看停止刷新的方法:

/**

* 根据尾部组件状态来停止刷新

* @param footerState

*

* 如果刷新完成,当前列表数据源是空的,就不显示尾部组件了。

* 这里这样做是因为通常列表无数据时,我们会显示一个空白页,如果再显示尾部组件如"没有更多数据了"就显得很多余

*/

endRefreshing(footerState: RefreshState) {

let footerRefreshState = footerState;

if (this.props.data.length === 0) {

footerRefreshState = RefreshState.Idle;

}

this.setState({

footerState: footerRefreshState,

isHeaderRefreshing: false,

isFooterRefreshing: false

})

}

这里传入一个尾部组件状态的参数是为了更新尾部组件的样式。同时对数据源data进行一个判断,如果为空说明当前没有数据,可以显示空白页面,那么尾部组件也没必要显示了。

以下是我使用RefreshListView实现的豆瓣电影页面分页加载的效果图:

完整的Demo地址: https://github.com/mrarronz/react-native-blog-examples/tree/master/Chapter4-PullRefresh/PullRefreshExample


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:接口测试一般怎么测(接口测试一般怎么测试)
下一篇:vue 组件 全局注册和局部注册的实现
相关文章

 发表评论

暂时没有评论,来抢沙发吧~