{"id":21057889,"url":"https://github.com/react-native-component/react-native-smart-pull-to-refresh-listview","last_synced_at":"2025-04-07T15:06:16.013Z","repository":{"id":57340213,"uuid":"63131585","full_name":"react-native-component/react-native-smart-pull-to-refresh-listview","owner":"react-native-component","description":"A smart pull-down-refresh and pull-up-loadmore react-native listview, for ios, written in pure JS, for android, written in JS and Java.","archived":false,"fork":false,"pushed_at":"2018-01-11T06:07:28.000Z","size":132,"stargazers_count":197,"open_issues_count":18,"forks_count":51,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-24T08:14:20.539Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/react-native-component.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-12T06:14:18.000Z","updated_at":"2024-11-06T20:43:49.000Z","dependencies_parsed_at":"2022-08-26T03:42:01.474Z","dependency_job_id":null,"html_url":"https://github.com/react-native-component/react-native-smart-pull-to-refresh-listview","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/react-native-component%2Freact-native-smart-pull-to-refresh-listview","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/react-native-component%2Freact-native-smart-pull-to-refresh-listview/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/react-native-component%2Freact-native-smart-pull-to-refresh-listview/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/react-native-component%2Freact-native-smart-pull-to-refresh-listview/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/react-native-component","download_url":"https://codeload.github.com/react-native-component/react-native-smart-pull-to-refresh-listview/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246481005,"owners_count":20784458,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-19T17:05:34.896Z","updated_at":"2025-03-31T14:11:05.651Z","avatar_url":"https://github.com/react-native-component.png","language":"JavaScript","funding_links":[],"categories":["组件"],"sub_categories":["ListView\u0026ScrollView"],"readme":"# react-native-smart-pull-to-refresh-listview\n\n[![npm](https://img.shields.io/npm/v/react-native-smart-pull-to-refresh-listview.svg)](https://www.npmjs.com/package/react-native-smart-pull-to-refresh-listview)\n[![npm](https://img.shields.io/npm/dm/react-native-smart-pull-to-refresh-listview.svg)](https://www.npmjs.com/package/react-native-smart-pull-to-refresh-listview)\n[![npm](https://img.shields.io/npm/dt/react-native-smart-pull-to-refresh-listview.svg)](https://www.npmjs.com/package/react-native-smart-pull-to-refresh-listview)\n[![npm](https://img.shields.io/npm/l/react-native-smart-pull-to-refresh-listview.svg)](https://github.com/react-native-component/react-native-smart-pull-to-refresh-listview/blob/master/LICENSE)\n\nA smart pull-down-refresh and pull-up-loadmore react-native listview,\nfor ios, written in pure JS, for android, written in JS and Java.\n\nThis component is compatible with React Native 0.25 and newer.\n\n## Preview\n\n![react-native-pull-to-refresh-listview-preview-ios][1]\n![react-native-pull-to-refresh-listview-preview-android][2]\n\n## Advanced Features\n\n* Flexible pull to refresh control for ios and android,\n\neasy to customize the 'RefreshView' style and content,\nbounce effect for both pull down refresh and pull up load more,\nif you want, you can also use the 'autoLoad' mode for pull up load more.\n[demonstration][101]\n\n* Memory management for ios and android,\n\nif you want, the listRow can remove its children to release memory when its position is outside viewport of device,\nand will undo when its position is inside viewport of device.\n[demonstration][102]\n\n* Extended support sticky header for android\n\nit also supports sticky header with pull to refresh\n[demonstration][103]\n\n\n## Installation\n\n```\nnpm install react-native-smart-pull-to-refresh-listview --save\n```\n\n## Installation (Android)\n\n* In `android/settings.gradle`\n\n```\n...\ninclude ':react-native-smart-swipe-refresh-layout'\nproject(':react-native-smart-swipe-refresh-layout').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-smart-pull-to-refresh-listview/android')\n```\n\n* In `android/app/build.gradle`\n\n```\n...\ndependencies {\n    ...\n    // From node_modules\n    compile project(':react-native-smart-swipe-refresh-layout')\n}\n```\n\n* In MainApplication.java\n\n```\n...\nimport com.reactnativecomponent.swiperefreshlayout.RCTSwipeRefreshLayoutPackage;    //import package\n...\n/**\n * A list of packages used by the app. If the app uses additional views\n * or modules besides the default ones, add more packages here.\n */\n@Override\nprotected List\u003cReactPackage\u003e getPackages() {\n    return Arrays.\u003cReactPackage\u003easList(\n        new MainReactPackage(),\n        new RCTSwipeRefreshLayoutPackage()  //register Module\n    );\n}\n...\n\n```\n\n* If you're using react-native 0.30-, follow these extra steps\n\n    * In node_modules/react-native-smart-pull-to-refresh-listview/android/src/main/java/com/reactnativecomponent/swiperefreshlayout/\n\n        * In TouchEvent.java\n\n            ```\n            ...\n            public TouchEvent(int viewTag, long timestampMs, int movement) {\n                super(viewTag, timestampMs);    //for older version\n                //super(viewTag);                 //for newer version\n                this.movement = movement;\n            }\n            ...\n            ```\n        *  In TouchUpEvent.java\n\n            ```\n            ...\n            public TouchUpEvent(int viewTag, long timestampMs) {\n                super(viewTag, timestampMs);  //for older verion\n                //super(viewTag);                 //for newer version\n            }\n            ...\n            ```\n\n## Full Demo\n\nsee [ReactNativeComponentDemos][0]\n\n## Usage\n\n```js\nimport React, {\n    Component,\n} from 'react'\nimport {\n    View,\n    Text,\n    StyleSheet,\n    Alert,\n    ScrollView,\n    ListView,\n    Image,\n    ActivityIndicator,\n    ProgressBarAndroid,\n    ActivityIndicatorIOS,\n    Platform,\n} from 'react-native'\n\nimport TimerEnhance from 'react-native-smart-timer-enhance'\nimport PullToRefreshListView from 'react-native-smart-pull-to-refresh-listview'\n\nexport default class PullToRefreshListViewDemo extends Component {\n\n    // 构造\n      constructor(props) {\n        super(props);\n\n        this._dataSource = new ListView.DataSource({\n            rowHasChanged: (r1, r2) =\u003e r1 !== r2,\n            //sectionHeaderHasChanged: (s1, s2) =\u003e s1 !== s2,\n        });\n\n      let dataList = []\n\n        this.state = {\n            first: true,\n            dataList: dataList,\n            dataSource: this._dataSource.cloneWithRows(dataList),\n        }\n    }\n\n    componentDidMount () {\n        this._pullToRefreshListView.beginRefresh()\n    }\n\n    //Using ListView\n    render() {\n        return (\n            \u003cPullToRefreshListView\n                ref={ (component) =\u003e this._pullToRefreshListView = component }\n                viewType={PullToRefreshListView.constants.viewType.listView}\n                contentContainerStyle={{backgroundColor: 'yellow', }}\n                style={{marginTop: Platform.OS == 'ios' ? 64 : 56, }}\n                initialListSize={20}\n                enableEmptySections={true}\n                dataSource={this.state.dataSource}\n                pageSize={20}\n                renderRow={this._renderRow}\n                renderHeader={this._renderHeader}\n                renderFooter={this._renderFooter}\n                //renderSeparator={(sectionID, rowID) =\u003e \u003cView style={styles.separator} /\u003e}\n                onRefresh={this._onRefresh}\n                onLoadMore={this._onLoadMore}\n                pullUpDistance={35}\n                pullUpStayDistance={50}\n                pullDownDistance={35}\n                pullDownStayDistance={50}\n            /\u003e\n        )\n\n    }\n\n    _renderRow = (rowData, sectionID, rowID) =\u003e {\n        return (\n            \u003cView style={styles.thumbnail}\u003e\n                \u003cView style={styles.textContainer}\u003e\n                    \u003cText\u003e{rowData.text}\u003c/Text\u003e\n                \u003c/View\u003e\n            \u003c/View\u003e\n        )\n    }\n\n    _renderHeader = (viewState) =\u003e {\n        let {pullState, pullDistancePercent} = viewState\n        let {refresh_none, refresh_idle, will_refresh, refreshing,} = PullToRefreshListView.constants.viewState\n        pullDistancePercent = Math.round(pullDistancePercent * 100)\n        switch(pullState) {\n            case refresh_none:\n                return (\n                    \u003cView style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}\u003e\n                        \u003cText\u003epull down to refresh\u003c/Text\u003e\n                    \u003c/View\u003e\n                )\n            case refresh_idle:\n                return (\n                    \u003cView style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}\u003e\n                        \u003cText\u003epull down to refresh{pullDistancePercent}%\u003c/Text\u003e\n                    \u003c/View\u003e\n                )\n            case will_refresh:\n                return (\n                    \u003cView style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}\u003e\n                        \u003cText\u003erelease to refresh{pullDistancePercent \u003e 100 ? 100 : pullDistancePercent}%\u003c/Text\u003e\n                    \u003c/View\u003e\n                )\n            case refreshing:\n                return (\n                    \u003cView style={{flexDirection: 'row', height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}\u003e\n                        {this._renderActivityIndicator()}\u003cText\u003erefreshing\u003c/Text\u003e\n                    \u003c/View\u003e\n                )\n        }\n    }\n\n    _renderFooter = (viewState) =\u003e {\n        let {pullState, pullDistancePercent} = viewState\n        let {load_more_none, load_more_idle, will_load_more, loading_more, loaded_all, } = PullToRefreshListView.constants.viewState\n        pullDistancePercent = Math.round(pullDistancePercent * 100)\n        switch(pullState) {\n            case load_more_none:\n                return (\n                    \u003cView style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}\u003e\n                        \u003cText\u003epull up to load more\u003c/Text\u003e\n                    \u003c/View\u003e\n                )\n            case load_more_idle:\n                return (\n                    \u003cView style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}\u003e\n                        \u003cText\u003epull up to load more{pullDistancePercent}%\u003c/Text\u003e\n                    \u003c/View\u003e\n                )\n            case will_load_more:\n                return (\n                    \u003cView style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}\u003e\n                        \u003cText\u003erelease to load more{pullDistancePercent \u003e 100 ? 100 : pullDistancePercent}%\u003c/Text\u003e\n                    \u003c/View\u003e\n                )\n            case loading_more:\n                return (\n                    \u003cView style={{flexDirection: 'row', height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}\u003e\n                        {this._renderActivityIndicator()}\u003cText\u003eloading\u003c/Text\u003e\n                    \u003c/View\u003e\n                )\n            case loaded_all:\n                return (\n                    \u003cView style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}\u003e\n                        \u003cText\u003eno more\u003c/Text\u003e\n                    \u003c/View\u003e\n                )\n        }\n    }\n\n    _onRefresh = () =\u003e {\n        //console.log('outside _onRefresh start...')\n\n        //simulate request data\n        this.setTimeout( () =\u003e {\n\n            //console.log('outside _onRefresh end...')\n            let addNum = 20\n            let refreshedDataList = []\n            for(let i = 0; i \u003c addNum; i++) {\n                refreshedDataList.push({\n                    text: `item-${i}`\n                })\n            }\n\n            this.setState({\n                dataList: refreshedDataList,\n                dataSource: this._dataSource.cloneWithRows(refreshedDataList),\n            })\n            this._pullToRefreshListView.endRefresh()\n\n        }, 3000)\n    }\n\n    _onLoadMore = () =\u003e {\n        //console.log('outside _onLoadMore start...')\n\n        this.setTimeout( () =\u003e {\n\n            //console.log('outside _onLoadMore end...')\n\n            let length = this.state.dataList.length\n            let addNum = 20\n            let addedDataList = []\n            if(length \u003e= 100) {\n                addNum = 3\n            }\n            for(let i = length; i \u003c length + addNum; i++) {\n                addedDataList.push({\n                    text: `item-${i}`\n                })\n            }\n            let newDataList = this.state.dataList.concat(addedDataList)\n            this.setState({\n                dataList: newDataList,\n                dataSource: this._dataSource.cloneWithRows(newDataList),\n            })\n\n            let loadedAll\n            if(length \u003e= 100) {\n                loadedAll = true\n                this._pullToRefreshListView.endLoadMore(loadedAll)\n            }\n            else {\n                loadedAll = false\n                this._pullToRefreshListView.endLoadMore(loadedAll)\n            }\n\n        }, 3000)\n    }\n\n    _renderActivityIndicator() {\n        return ActivityIndicator ? (\n            \u003cActivityIndicator\n                style={{marginRight: 10,}}\n                animating={true}\n                color={'#ff0000'}\n                size={'small'}/\u003e\n        ) : Platform.OS == 'android' ?\n            (\n                \u003cProgressBarAndroid\n                    style={{marginRight: 10,}}\n                    color={'#ff0000'}\n                    styleAttr={'Small'}/\u003e\n\n            ) :  (\n            \u003cActivityIndicatorIOS\n                style={{marginRight: 10,}}\n                animating={true}\n                color={'#ff0000'}\n                size={'small'}/\u003e\n        )\n    }\n\n}\n\n\n\nconst styles = StyleSheet.create({\n    itemHeader: {\n        height: 35,\n        borderBottomWidth: StyleSheet.hairlineWidth,\n        borderBottomColor: '#ccc',\n        backgroundColor: 'blue',\n        overflow: 'hidden',\n        justifyContent: 'center',\n        alignItems: 'center',\n    },\n    item: {\n        height: 60,\n        //borderBottomWidth: StyleSheet.hairlineWidth,\n        //borderBottomColor: '#ccc',\n        overflow: 'hidden',\n        justifyContent: 'center',\n        alignItems: 'center',\n    },\n\n    contentContainer: {\n        paddingTop: 20 + 44,\n    },\n\n    thumbnail: {\n        padding: 6,\n        flexDirection: 'row',\n        borderBottomWidth: StyleSheet.hairlineWidth,\n        borderBottomColor: '#ccc',\n        overflow: 'hidden',\n    },\n\n    textContainer: {\n        padding: 20,\n        flex: 1,\n        justifyContent: 'center',\n        alignItems: 'center',\n    }\n})\n\nexport default TimerEnhance(PullToRefreshListViewDemo)\n```\n\n## Props\n\nProp                    | Type   | Optional | Default   | Description\n----------------------- | ------ | -------- | --------- | -----------\n...ListView.propTypes   |        |          |           | see [react-native documents][3]\nviewType                | enum   | Yes      | Symbol    | determines the viewType which will be used(ScrollView, ListView)\nautoLoadMore            | bool   | Yes      | false     | when the value is true, pull up load more will be auto\nonRefresh               | func   | Yes      |           | when refreshing, this function will be called\nonLoadMore              | func   | Yes      |           | when loadingMore, this function will be called\nonEndReachedThreshold   | number | Yes      | 0         | threshold in pixels (virtual, not physical) for calling onLoadMore\npullUpDistance          | number | Yes      | 35        | determines the pull up max distance\npullUpStayDistance      | number | Yes      | 50        | determines the pull up stay distance\npullDownDistance        | number | Yes      | 35        | determines the pull down max distance\npullDownStayDistance    | number | Yes      | 50        | determines the pull down stay distance\nenabledPullUp           | bool   | Yes      | true      | when the value is false, pull up load more will be disabled\nenabledPullDown         | bool   | Yes      | true      | when the value is false, pull down refresh will be disabled\nlistItemProps           | object | Yes      |           | see [react-native documents][4]\nrenderRowWithVisibility | bool   | Yes      |           | when the value is true, the children of the listRow can be controlled with 'hidden' state\npageTop                 | number | Yes      | 0         | determines the top relative to the page of the float section header(sticky header) view\nfloatSectionHeaderWidth | number | Yes      |deviceWidth| determines the width of the float section header(sticky header) view\nrenderFloatSectionHeader| number | Yes      | 0         | determines the width of the float section header(sticky header) view\nlistSectionProps        | object | Yes      |           | see [react-native documents][4]\n\n## Special Props\n\n* listItemProps: when set this prop, listView will use special 'listRow',\nthe listRow will remove its children to release memory when its position is outside viewport of device,\nand will undo when its position is inside viewport of device.\nUsually it is used with 'react-native-smart-image-loader'\n\n* renderRowWithVisibility: when the value is true,\nthe children of the listRow can be controlled with 'hidden' state.\nThis prop is valid when 'listItemProps' is being set, and it is only valid for android.\nUsually it is used with 'react-native-smart-image-loader'\n\n* pageTop, floatSectionHeaderWidth, renderFloatSectionHeader, listSectionProps\nare used for android to support ios-like sticky header\n\n## Method\n\n* beginRefresh(bounceDisabled): force begin pull down refresh, if bounceDisabled is true, the bounce animation will be disabled\n* endRefresh(bounceDisabled): end pull down refresh, if bounceDisabled is true, the bounce animation will be disabled\n* endLoadMore: end pull up load more\n\n\n[0]: https://github.com/cyqresig/ReactNativeComponentDemos\n[1]: http://cyqresig.github.io/img/react-native-smart-pull-to-refresh-preview-v1.6.0.gif\n[2]: http://cyqresig.github.io/img/react-native-smart-pull-to-refresh-preview-android-v1.6.0.gif\n[3]: http://facebook.github.io/react-native/docs/listview.html#props\n[4]: http://facebook.github.io/react-native/docs/view.html#props\n\n[101]: http://cyqresig.github.io/img/pull-to-refresh-flexible-pull-to-refresh-control.gif\n[102]: http://cyqresig.github.io/img/pull-to-refresh-memory-management.gif\n[103]: http://cyqresig.github.io/img/pull-to-refresh-extended-support-sticky-header-for-android.gif","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freact-native-component%2Freact-native-smart-pull-to-refresh-listview","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freact-native-component%2Freact-native-smart-pull-to-refresh-listview","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freact-native-component%2Freact-native-smart-pull-to-refresh-listview/lists"}