博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React Native 之自定义下拉刷新
阅读量:4085 次
发布时间:2019-05-25

本文共 7940 字,大约阅读时间需要 26 分钟。

关于React Native的下拉刷新,虽然官方出了一个控件RefreshControl,但可定制性太差,基本上样式固定了.最近将公司的Android端用RN重写一下,之前用的是PullToRefresh,于是准备用React Native写一套该效果出来.

http://www.jianshu.com/p/9a4151852722

效果图:



PullToRefresh类似,不光是ListView控件可以下拉刷新,任一控件都可以.


实现原理

PanResponder

在React Native的API中有一个PanResponder,它能检测到用户的手势,与Android中的 事件分发机制的作用类似.可以捕获到用户的touch down,touch move,touch up事件. 然后根据用户按下的距离,对整个View进行y轴的移动.

setNativeProps

在捕获到用户的下拉轨迹后,需要 子控件 跟随用户手势,这里可以改变子控件的marginTop或者translateY的值.
这里比较适合用setNativeProps来直接改变值,如果用state状态机来动态更改,会造成 View的多次重复render,造成不必要的性能损耗.

LayoutAnimation

当用户刷新动作完成之后,需要程序自动将 正在刷新的布局恢复原状,这里使用LayoutAnimation可以很简单的做到.

shouldComponentUpdate

该函数 可用来比较哪些状态的更改需要重新render,用当前的state与将要改变的state比较是否一致.

完整源码:

'use strict'import React, { Component } from 'react';import {    AppRegistry,    StyleSheet,    View,    PanResponder,    LayoutAnimation,    ProgressBarAndroid,    Dimensions,    Text,    AsyncStorage,    Image} from 'react-native';let self;/**ref的引用*/const PULL_REFRESH_LAYOUT="pullLayout";/**屏幕宽度*/const deviceWidth = Dimensions.get('window').width;/**下拉阻力系数*/const factor=1.8;/**最大下拉高度*/const MAX_PULL_LENGTH=170;/**Loading的高度*/const REFRESH_PULL_LENGTH=70;/**动画时长*/const BACK_TIME=400;/**存储最后刷新时间的Key*/const REFRESH_LAST_TIME_KEY="refresh_last";const RefreshStatus={    Refresh_NONE:0,    Refresh_Drag_Down:1,    Refresh_Loading:2,    Refresh_Reset:3,};const ShowLoadingStatus={    SHOW_DOWN:0,    SHOW_UP:1,    SHOW_LOADING:2,};class PullToRefreshLayout extends Component{    _panResponder:{}    // 构造    constructor(props) {        super(props);        // 初始状态        this.state = {            currentDistance:0,            pullRefreshStatus:RefreshStatus.Refresh_NONE,            showPullStatus:ShowLoadingStatus.SHOW_DOWN,            showPullLastTime:'NONE',        };        this.resetHeader=this.resetHeader.bind(this);        this.refreshStateHeader=this.refreshStateHeader.bind(this);        this.getTime=this.getTime.bind(this);        this.addZeroAtFront=this.addZeroAtFront.bind(this);    }    //要求成为响应者    _handleStartShouldSetPanResponder(e: Object, gestureState: Object): boolean {        return true;    }    _handleMoveShouldSetPanResponder(e: Object, gestureState: Object): boolean {        return true;    }    //touch down 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!    _handlePanResponderGrant(e: Object, gestureState: Object){    }    //touch move 响应滑动事件    _handlePanResponderMove(e: Object, gestureState: Object) {        if(self.state.currentDistance>REFRESH_PULL_LENGTH){            if(self.state.showPullStatus===ShowLoadingStatus.SHOW_DOWN){                self.setState({                    showPullStatus:ShowLoadingStatus.SHOW_UP,                });            }        }        else{            if (self.state.showPullStatus===ShowLoadingStatus.SHOW_UP){                self.setState({                    showPullStatus:ShowLoadingStatus.SHOW_DOWN,                });            }        }        if (self.state.pullRefreshStatus===RefreshStatus.Refresh_Loading){            self.setState({                currentDistance:REFRESH_PULL_LENGTH+gestureState.dy/factor,                // refreshStateHeader:2,            });            self.refs[PULL_REFRESH_LAYOUT].setNativeProps({                style:{                    marginTop:self.state.currentDistance,                }            });            return;        }        if (gestureState.dy>0&&self.state.currentDistance
0&&self.state.currentDistance>MAX_PULL_LENGTH){
//则不再往下移动 self.setState({ currentDistance:MAX_PULL_LENGTH, pullRefreshStatus:RefreshStatus.Refresh_Drag_Down, }); self.refs[PULL_REFRESH_LAYOUT].setNativeProps({ style:{ marginTop:self.state.currentDistance, } }); } } resetHeader(){ LayoutAnimation.configureNext({ duration: BACK_TIME, update: { type: 'linear', } }); self.refs[PULL_REFRESH_LAYOUT].setNativeProps({ style:{ marginTop:0, } }); self.setState({ currentDistance:0, pullRefreshStatus:RefreshStatus.Refresh_Reset, showPullStatus:ShowLoadingStatus.SHOW_DOWN, }); } refreshStateHeader(){ self.setState({ pullRefreshStatus:RefreshStatus.Refresh_Loading, currentDistance:REFRESH_PULL_LENGTH, showPullStatus:ShowLoadingStatus.SHOW_LOADING, },()=>{ if(self.props.onRefresh){ self.props.onRefresh(); } }); LayoutAnimation.configureNext({ duration: BACK_TIME, update: { type: 'linear', } }); self.refs[PULL_REFRESH_LAYOUT].setNativeProps({ style:{ marginTop:REFRESH_PULL_LENGTH, } }); } addZeroAtFront(count){ if (count<10){ count="0"+count; } return count; } getTime(){ let date=new Date(); let mMonth=this.addZeroAtFront(date.getMonth()+1); let mDate=this.addZeroAtFront(date.getDate()); let mHours=this.addZeroAtFront(date.getHours()); let mMinutes=this.addZeroAtFront(date.getMinutes()); return mMonth+"-"+mDate+" "+mHours+":"+mMinutes; } stopRefresh(){ let savedDate=this.getTime(); self.setState({ showPullLastTime:savedDate, }); AsyncStorage.setItem(REFRESH_LAST_TIME_KEY,savedDate,()=>{ }); this.resetHeader(); } _handlePanResponderEnd(e: Object, gestureState: Object) { if (self.state.currentDistance>=REFRESH_PULL_LENGTH){ self.refreshStateHeader(); } else{ self.resetHeader(); } } componentDidMount() { AsyncStorage.getItem(REFRESH_LAST_TIME_KEY,(err,result)=>{ if (result){ self.setState({ showPullLastTime:result, }); } }); } componentWillMount() { self=this; this._panResponder=PanResponder.create({ onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder, onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder, onPanResponderGrant: this._handlePanResponderGrant, onPanResponderMove: this._handlePanResponderMove, onPanResponderRelease: this._handlePanResponderEnd, onPanResponderTerminate: this._handlePanResponderEnd, }); } shouldComponentUpdate(nextProps,nextState) { if (nextState.showPullStatus!==self.state.showPullStatus){ return true; } if (self.state.showPullLastTime!==nextState.showPullLastTime){ return true; } return false; } render(){ let pullText; let indicatorView; if (this.state.showPullStatus===ShowLoadingStatus.SHOW_DOWN){ indicatorView=
; pullText="下拉刷新"; } else if (this.state.showPullStatus===ShowLoadingStatus.SHOW_UP){ indicatorView=
; pullText="释放刷新"; } else if(this.state.showPullStatus===ShowLoadingStatus.SHOW_LOADING){ indicatorView=
pullText="刷新中......"; } return (
{indicatorView}
{pullText}
最后更新: { this.state.showPullLastTime}
{
this.props.children}
); }}export default PullToRefreshLayout;var styles = StyleSheet.create({ base: { flex: 1, position :'relative' },});

使用代码

转载地址:http://umhni.baihongyu.com/

你可能感兴趣的文章
phpquery抓取网站内容简单介绍
查看>>
找工作准备的方向(4月22日写的)
查看>>
关于fwrite写入文件后打开查看是乱码的问题
查看>>
用结构体指针前必须要用malloc,不然会出现段错误
查看>>
Linux系统中的美
查看>>
一些实战项目(linux应用层编程,多线程编程,网络编程)
查看>>
我觉得专注于去学东西就好了,与世无争。
查看>>
原来k8s docker是用go语言写的,和现在所讲的go是一个东西!
查看>>
STM32CubeMX 真的不要太好用
查看>>
STM32CubeMX介绍、下载与安装
查看>>
不要买铝合金机架的无人机,不耐摔,易变形弯曲。
查看>>
ACfly也是基于FreeRTOS的
查看>>
F330装GPS的位置
查看>>
pixhawk也可以用Airsim仿真
查看>>
《无人机电机与电调技术》可以看看
查看>>
我发现七月在线的GAAS课程基本都讲到了
查看>>
电机堵转
查看>>
carzepony也在想往FreeRTOS上迁移
查看>>
可以买个好点的电烙铁
查看>>
ACfly调参记录(包括ACfly-F330和ACfly-T265)
查看>>