Code Monkey home page Code Monkey logo

react-native-smart-pull-to-refresh-listview's Introduction

react-native-smart-pull-to-refresh-listview

npm npm npm npm

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.

This component is compatible with React Native 0.25 and newer.

Preview

react-native-pull-to-refresh-listview-preview-ios react-native-pull-to-refresh-listview-preview-android

Advanced Features

  • Flexible pull to refresh control for ios and android,

easy to customize the 'RefreshView' style and content, bounce effect for both pull down refresh and pull up load more, if you want, you can also use the 'autoLoad' mode for pull up load more. demonstration

  • Memory management for ios and android,

if you want, the listRow can remove its children to release memory when its position is outside viewport of device, and will undo when its position is inside viewport of device. demonstration

  • Extended support sticky header for android

it also supports sticky header with pull to refresh demonstration

Installation

npm install react-native-smart-pull-to-refresh-listview --save

Installation (Android)

  • In android/settings.gradle
...
include ':react-native-smart-swipe-refresh-layout'
project(':react-native-smart-swipe-refresh-layout').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-smart-pull-to-refresh-listview/android')
  • In android/app/build.gradle
...
dependencies {
    ...
    // From node_modules
    compile project(':react-native-smart-swipe-refresh-layout')
}
  • In MainApplication.java
...
import com.reactnativecomponent.swiperefreshlayout.RCTSwipeRefreshLayoutPackage;    //import package
...
/**
 * A list of packages used by the app. If the app uses additional views
 * or modules besides the default ones, add more packages here.
 */
@Override
protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
        new MainReactPackage(),
        new RCTSwipeRefreshLayoutPackage()  //register Module
    );
}
...

  • If you're using react-native 0.30-, follow these extra steps

    • In node_modules/react-native-smart-pull-to-refresh-listview/android/src/main/java/com/reactnativecomponent/swiperefreshlayout/

      • In TouchEvent.java

        ...
        public TouchEvent(int viewTag, long timestampMs, int movement) {
            super(viewTag, timestampMs);    //for older version
            //super(viewTag);                 //for newer version
            this.movement = movement;
        }
        ...
        
      • In TouchUpEvent.java

        ...
        public TouchUpEvent(int viewTag, long timestampMs) {
            super(viewTag, timestampMs);  //for older verion
            //super(viewTag);                 //for newer version
        }
        ...
        

Full Demo

see ReactNativeComponentDemos

Usage

import React, {
    Component,
} from 'react'
import {
    View,
    Text,
    StyleSheet,
    Alert,
    ScrollView,
    ListView,
    Image,
    ActivityIndicator,
    ProgressBarAndroid,
    ActivityIndicatorIOS,
    Platform,
} from 'react-native'

import TimerEnhance from 'react-native-smart-timer-enhance'
import PullToRefreshListView from 'react-native-smart-pull-to-refresh-listview'

export default class PullToRefreshListViewDemo extends Component {

    // 构造
      constructor(props) {
        super(props);

        this._dataSource = new ListView.DataSource({
            rowHasChanged: (r1, r2) => r1 !== r2,
            //sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
        });

      let dataList = []

        this.state = {
            first: true,
            dataList: dataList,
            dataSource: this._dataSource.cloneWithRows(dataList),
        }
    }

    componentDidMount () {
        this._pullToRefreshListView.beginRefresh()
    }

    //Using ListView
    render() {
        return (
            <PullToRefreshListView
                ref={ (component) => this._pullToRefreshListView = component }
                viewType={PullToRefreshListView.constants.viewType.listView}
                contentContainerStyle={{backgroundColor: 'yellow', }}
                style={{marginTop: Platform.OS == 'ios' ? 64 : 56, }}
                initialListSize={20}
                enableEmptySections={true}
                dataSource={this.state.dataSource}
                pageSize={20}
                renderRow={this._renderRow}
                renderHeader={this._renderHeader}
                renderFooter={this._renderFooter}
                //renderSeparator={(sectionID, rowID) => <View style={styles.separator} />}
                onRefresh={this._onRefresh}
                onLoadMore={this._onLoadMore}
                pullUpDistance={35}
                pullUpStayDistance={50}
                pullDownDistance={35}
                pullDownStayDistance={50}
            />
        )

    }

    _renderRow = (rowData, sectionID, rowID) => {
        return (
            <View style={styles.thumbnail}>
                <View style={styles.textContainer}>
                    <Text>{rowData.text}</Text>
                </View>
            </View>
        )
    }

    _renderHeader = (viewState) => {
        let {pullState, pullDistancePercent} = viewState
        let {refresh_none, refresh_idle, will_refresh, refreshing,} = PullToRefreshListView.constants.viewState
        pullDistancePercent = Math.round(pullDistancePercent * 100)
        switch(pullState) {
            case refresh_none:
                return (
                    <View style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}>
                        <Text>pull down to refresh</Text>
                    </View>
                )
            case refresh_idle:
                return (
                    <View style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}>
                        <Text>pull down to refresh{pullDistancePercent}%</Text>
                    </View>
                )
            case will_refresh:
                return (
                    <View style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}>
                        <Text>release to refresh{pullDistancePercent > 100 ? 100 : pullDistancePercent}%</Text>
                    </View>
                )
            case refreshing:
                return (
                    <View style={{flexDirection: 'row', height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}>
                        {this._renderActivityIndicator()}<Text>refreshing</Text>
                    </View>
                )
        }
    }

    _renderFooter = (viewState) => {
        let {pullState, pullDistancePercent} = viewState
        let {load_more_none, load_more_idle, will_load_more, loading_more, loaded_all, } = PullToRefreshListView.constants.viewState
        pullDistancePercent = Math.round(pullDistancePercent * 100)
        switch(pullState) {
            case load_more_none:
                return (
                    <View style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}>
                        <Text>pull up to load more</Text>
                    </View>
                )
            case load_more_idle:
                return (
                    <View style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}>
                        <Text>pull up to load more{pullDistancePercent}%</Text>
                    </View>
                )
            case will_load_more:
                return (
                    <View style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}>
                        <Text>release to load more{pullDistancePercent > 100 ? 100 : pullDistancePercent}%</Text>
                    </View>
                )
            case loading_more:
                return (
                    <View style={{flexDirection: 'row', height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}>
                        {this._renderActivityIndicator()}<Text>loading</Text>
                    </View>
                )
            case loaded_all:
                return (
                    <View style={{height: 35, justifyContent: 'center', alignItems: 'center', backgroundColor: 'pink',}}>
                        <Text>no more</Text>
                    </View>
                )
        }
    }

    _onRefresh = () => {
        //console.log('outside _onRefresh start...')

        //simulate request data
        this.setTimeout( () => {

            //console.log('outside _onRefresh end...')
            let addNum = 20
            let refreshedDataList = []
            for(let i = 0; i < addNum; i++) {
                refreshedDataList.push({
                    text: `item-${i}`
                })
            }

            this.setState({
                dataList: refreshedDataList,
                dataSource: this._dataSource.cloneWithRows(refreshedDataList),
            })
            this._pullToRefreshListView.endRefresh()

        }, 3000)
    }

    _onLoadMore = () => {
        //console.log('outside _onLoadMore start...')

        this.setTimeout( () => {

            //console.log('outside _onLoadMore end...')

            let length = this.state.dataList.length
            let addNum = 20
            let addedDataList = []
            if(length >= 100) {
                addNum = 3
            }
            for(let i = length; i < length + addNum; i++) {
                addedDataList.push({
                    text: `item-${i}`
                })
            }
            let newDataList = this.state.dataList.concat(addedDataList)
            this.setState({
                dataList: newDataList,
                dataSource: this._dataSource.cloneWithRows(newDataList),
            })

            let loadedAll
            if(length >= 100) {
                loadedAll = true
                this._pullToRefreshListView.endLoadMore(loadedAll)
            }
            else {
                loadedAll = false
                this._pullToRefreshListView.endLoadMore(loadedAll)
            }

        }, 3000)
    }

    _renderActivityIndicator() {
        return ActivityIndicator ? (
            <ActivityIndicator
                style={{marginRight: 10,}}
                animating={true}
                color={'#ff0000'}
                size={'small'}/>
        ) : Platform.OS == 'android' ?
            (
                <ProgressBarAndroid
                    style={{marginRight: 10,}}
                    color={'#ff0000'}
                    styleAttr={'Small'}/>

            ) :  (
            <ActivityIndicatorIOS
                style={{marginRight: 10,}}
                animating={true}
                color={'#ff0000'}
                size={'small'}/>
        )
    }

}



const styles = StyleSheet.create({
    itemHeader: {
        height: 35,
        borderBottomWidth: StyleSheet.hairlineWidth,
        borderBottomColor: '#ccc',
        backgroundColor: 'blue',
        overflow: 'hidden',
        justifyContent: 'center',
        alignItems: 'center',
    },
    item: {
        height: 60,
        //borderBottomWidth: StyleSheet.hairlineWidth,
        //borderBottomColor: '#ccc',
        overflow: 'hidden',
        justifyContent: 'center',
        alignItems: 'center',
    },

    contentContainer: {
        paddingTop: 20 + 44,
    },

    thumbnail: {
        padding: 6,
        flexDirection: 'row',
        borderBottomWidth: StyleSheet.hairlineWidth,
        borderBottomColor: '#ccc',
        overflow: 'hidden',
    },

    textContainer: {
        padding: 20,
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    }
})

export default TimerEnhance(PullToRefreshListViewDemo)

Props

Prop Type Optional Default Description
...ListView.propTypes see react-native documents
viewType enum Yes Symbol determines the viewType which will be used(ScrollView, ListView)
autoLoadMore bool Yes false when the value is true, pull up load more will be auto
onRefresh func Yes when refreshing, this function will be called
onLoadMore func Yes when loadingMore, this function will be called
onEndReachedThreshold number Yes 0 threshold in pixels (virtual, not physical) for calling onLoadMore
pullUpDistance number Yes 35 determines the pull up max distance
pullUpStayDistance number Yes 50 determines the pull up stay distance
pullDownDistance number Yes 35 determines the pull down max distance
pullDownStayDistance number Yes 50 determines the pull down stay distance
enabledPullUp bool Yes true when the value is false, pull up load more will be disabled
enabledPullDown bool Yes true when the value is false, pull down refresh will be disabled
listItemProps object Yes see react-native documents
renderRowWithVisibility bool Yes when the value is true, the children of the listRow can be controlled with 'hidden' state
pageTop number Yes 0 determines the top relative to the page of the float section header(sticky header) view
floatSectionHeaderWidth number Yes deviceWidth determines the width of the float section header(sticky header) view
renderFloatSectionHeader number Yes 0 determines the width of the float section header(sticky header) view
listSectionProps object Yes see react-native documents

Special Props

  • listItemProps: when set this prop, listView will use special 'listRow', the listRow will remove its children to release memory when its position is outside viewport of device, and will undo when its position is inside viewport of device. Usually it is used with 'react-native-smart-image-loader'

  • renderRowWithVisibility: when the value is true, the children of the listRow can be controlled with 'hidden' state. This prop is valid when 'listItemProps' is being set, and it is only valid for android. Usually it is used with 'react-native-smart-image-loader'

  • pageTop, floatSectionHeaderWidth, renderFloatSectionHeader, listSectionProps are used for android to support ios-like sticky header

Method

  • beginRefresh(bounceDisabled): force begin pull down refresh, if bounceDisabled is true, the bounce animation will be disabled
  • endRefresh(bounceDisabled): end pull down refresh, if bounceDisabled is true, the bounce animation will be disabled
  • endLoadMore: end pull up load more

react-native-smart-pull-to-refresh-listview's People

Contributors

cyqresig avatar darwin-ming avatar jiasm avatar shiyunjie avatar yreenchan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-native-smart-pull-to-refresh-listview's Issues

无法上拉加载更多

环境: android

第一次上拉的时候显示了footer并且正常执行了onLoadMore , 加载到了数据之后, 继续向上拉,当第二次达到列表尾部时, 这时候footer被隐藏了没有执行onLoadMore

看了下源码,原因应该是

this._canLoadMore 这个变量第一次onLoadMore之后就被设置成false,导致之后无法继续加载

下面是我的代码:

<PullToRefreshListView
                viewType={PullToRefreshListView.constants.viewType.listView}
                ref={comp => this._pullToRefreshListView = comp}
                dataSource={this.state.ds}
                renderRow={this.renderRow.bind(this)}
                renderHeader={this.renderRefreshHeader.bind(this)}
                renderFooter={this.renderFooter.bind(this)}
                removeClippedSubviews={true}
                enableEmptySections
                pagingEnabled={false}
                pageSize={28}
                initialListSize={28}
                onEndReachedThreshold={10}
                onLoadMore={this.onLoadMore.bind(this)}
                onRefresh={this.onRefresh.bind(this)}
                autoLoadMore={true}
            >
</PullToRefreshListView>

Jumping when view is rendering

I have tried pull to refresh for scrollview and added content with in PullToRefreshListView.

Whenever the view is rendering, the entire view content in PullToRefreshListView, moving few points from bottom to Top. It looks like jumping whenever we render the screen. How to remove this jumping whenever view is rendering?

0.41版本支持?

Readme里写到只支持到0.40,但是我用0.41试了一下,也没发现有什么问题。
请问是什么原因只支持到40?有潜在的bug?

[iOS] Warning of RCTSwipeRefreshLayout

Find an warning on iOS platform.

ExceptionsManager.js:78 Warning: Native component for "RCTSwipeRefreshLayout" does not exist

the stack is:

reactConsoleError @ ExceptionsManager.js:78
printWarning @ warning.js:36
warning @ warning.js:60
requireNativeComponent @ requireNativeComponent.js:53
(anonymous function) @ AndroidSwipeRefreshLayout.js:48
loadModuleImplementation @ require.js:122
guardedLoadModule @ require.js:65
_require @ require.js:49
(anonymous function) @ PullToRefreshListView-android.js:36
loadModuleImplementation @ require.js:122
guardedLoadModule @ require.js:65
_require @ require.js:49
(anonymous function) @ PullToRefreshListView.js:13
loadModuleImplementation @ require.js:122

I find in the PullToRefreshListView.js:12~13.
It imports both iOS and Android view at the same time, that's the problem:

import AndroidPullToRefreshListView from './PullToRefreshListView-android'
import IOSPullToRefreshListView from './PullToRefreshListView-ios'

I think it would be better to divide PullToRefreshListView.js to two files: PullToRefreshListView.ios.js and PullToRefreshListView.android.js

refreshing状态下的布局怎么控制高度呢?

在refresh_none、refresh_idle、will_refresh这几个状态下header都能按照我写的布局显示,但是在refreshing状态下header设置固定高度后整个布局会自动往上靠,能看到的只有整个高度的一小部分

Error: Failed to crunch 造成这个原因百度出来的结果是路径名太长,clear 未解决

Error:Execution failed for task ':react-native-smart-swipe-refresh-layout:mergeDebugAndroidTestResources'.

Error: Failed to crunch file ...\node_modules\react-native-smart-pull-to-refresh-listview\android\build\intermediates\exploded-aar\com.android.support\appcompat-v7\23.0.1\res\drawable-xhdpi\abc_textfield_search_activated_mtrl_alpha.9.png into ...\node_modules\react-native-smart-pull-to-refresh-listview\android\build\intermediates\res\merged\androidTest\debug\drawable-xhdpi\abc_textfield_search_activated_mtrl_alpha.9.png

[Android] a piece of data?

安卓环境下 autoLoadMore={true}
如果只有一条数据的时候,这条数据具有点击事件,下拉的时候会直接触发点击事件。

setState方法变成了state对象

<PullToRefreshListView ref={ (component) => this.pullToRefreshListView = component } viewType={PullToRefreshListView.constants.viewType.listView} style={Style.flexAll} contentContainerStyle={{backgroundColor: 'white', }} initialListSize={20} enableEmptySections={true} dataSource={self.state.orderDataSource} pageSize={20} renderRow={this.renderOrderRow} renderHeader={this.renderHeader} renderFooter={this.renderFooter} //renderSeparator={(sectionID, rowID) => <View style={styles.separator} />} onRefresh={this.onRefresh} onLoadMore={this.onLoadMore} pullUpDistance={35} pullUpStayDistance={50} pullDownDistance={35} pullDownStayDistance={50} ></PullToRefreshListView>
在这里的onRefresh和onLoadMore是用箭头函数实现的,是自动绑定类的this,里面的props也是正常的数据,但是在这两个方法里用setState报错说setState是对象而不是方法,在这两个方法里打印setState和state,console.log('self.setState:',this.setState); console.log('self.state:',this.state);`得到的结果就是setState和state是有相同key的对象,不过对象的值不一样,我在这个类里的其它箭头函数里可以正常使用setState方法

[Android]android版本在Release版本,沒有數據的時候偶爾會宕機,debug版本沒有這問題

com.facebook.react.common.JavascriptException: null is not an object (evaluating 'e._footer.setNativeProps'), stack:
@451:10389
@20:120
value@30:3059
@30:1402
d@30:127
value@30:1372

                                                 at com.facebook.react.modules.core.ExceptionsManagerModule.showOrThrowError(ExceptionsManagerModule.java:97)
                                                 at com.facebook.react.modules.core.ExceptionsManagerModule.reportFatalException(ExceptionsManagerModule.java:81)
                                                 at java.lang.reflect.Method.invoke(Native Method)
                                                 at java.lang.reflect.Method.invoke(Method.java:372)
                                                 at com.facebook.react.bridge.BaseJavaModule$JavaMethod.invoke(BaseJavaModule.java:318)
                                                 at com.facebook.react.cxxbridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:158)
                                                 at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
                                                 at android.os.Handler.handleCallback(Handler.java:739)
                                                 at android.os.Handler.dispatchMessage(Handler.java:95)
                                                 at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:31)
                                                 at android.os.Looper.loop(Looper.java:135)
                                                 at com.facebook.react.bridge.queue.MessageQueueThreadImpl$3.run(MessageQueueThreadImpl.java:196)
                                                 at java.lang.Thread.run(Thread.java:818)

偶尔会出现页面渲染错误

每搜索一次都会更新datasource,然后有时候,页面会出错(要鼠标拉动一下,就会移上到顶部来,数据已经加载的了),这里只用了loadmore
image

[iOS] 添加listItemProps属性后,快速滑动偶尔出现空白

Hello!
我在看了组件的源码之后,发现如果提供了listItemProps属性之后,iOS端会做进一步的优化,即是屏幕之内可视的item才渲染,其他的都返回了null,详见代码

我在iOS端测试的时候,如果滑动速度较快,会有一定几率出现空白,估计是返回的item都变成null了。如果去掉了listItemProps属性的话,那么就一切正常。Android的也大概看了下源码,是基于原生实现的,目前没有发现这个问题。

关于这个问题,想请教下有没相关的解决思路参考下?

关开scrollViewTranslateY

上拉读取到数据以后,我认为可以不用回滚到原来的位置,,这样新的数据正好填充上拉出来的 footer 区域,不会让用户觉得新数据又隐藏了,当 loadAll 为 fase 时,,因为没有数据,自然也会回滚到原来位置!
我只是粗暴地去掉了 resetFooter 里scrollViewTranslateY,目前看上去工作的很好, 望指教

非常喜欢作者的组件,,我试了很多上下拉刷新组件,,这一个是最完美的!

[Android]

当嵌套在react-native-scrollable-tab-view里面的时候,下拉会出现和左右滑动冲突问题

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.