├── Images
└── pullview.gif
├── pull
├── i18n
│ ├── All.js
│ └── index.js
├── style
│ └── index.js
├── PullLayout.js
├── LoadingSpinner.js
├── PullView.js
├── PullList.js
├── index.js
└── Pullable.js
└── README.md
/Images/pullview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuyunqiang/react-native-pullview/HEAD/Images/pullview.gif
--------------------------------------------------------------------------------
/pull/i18n/All.js:
--------------------------------------------------------------------------------
1 | export default {
2 | "": {
3 | pulling: "pulling...",
4 | pullok: "pull ok......",
5 | pullrelease: "refreshing......"
6 | },
7 | "zh_CN": {
8 | pulling: "下拉刷新...",
9 | pullok: "松开刷新......",
10 | pullrelease: "玩命刷新中......"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/pull/style/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import {
4 | StyleSheet
5 | } from 'react-native';
6 |
7 | export default StyleSheet.create({
8 | wrap: {
9 | flex: 1,
10 | flexGrow: 1,
11 | flexDirection: 'column',
12 | zIndex:-999,
13 | },
14 | hide: {
15 | position: 'absolute',
16 | left: 10000
17 | },
18 | show: {
19 | position: 'relative',
20 | left: 0
21 | }
22 | });
23 |
--------------------------------------------------------------------------------
/pull/i18n/index.js:
--------------------------------------------------------------------------------
1 | import { Platform, NativeModules } from 'react-native';
2 | import all from './All.js';
3 |
4 | function getLocale() {
5 | try {
6 | if (Platform.OS === 'android') {
7 | return NativeModules.I18nManager.localeIdentifier;
8 | } else {
9 | return NativeModules.SettingsManager.settings.AppleLocale;
10 | }
11 | } catch (e) {
12 | return null;
13 | } finally {}
14 | }
15 | function getI18N(v) {
16 | let locale = getLocale();
17 | if(v[locale]) {
18 | return v[locale];
19 | } else if(v[""]) {
20 | return v[""];
21 | } else {
22 | return v["default"];
23 | }
24 | }
25 | var i18n = getI18N(all);
26 | export default i18n;
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PullView
2 | scrollview&&FlatList Pull refresh and loadmore
3 |
4 | 参考react-native-pull和RefreshListDemo。
5 | android&&ios都可以使用。
6 | # new
7 | android可以使用原生的下拉刷新效果会更好 如下使用:
8 | /**
9 | * PullScroll => scrollview
10 | * PullList =>flatlist
11 | * Android_Native 是否使用android原生下拉刷新组件 true开启
12 | * ****/
13 |
14 | 如果开启原生属性 需要android引入原生模块
15 | 下拉刷新数据传送的方式有两种
16 | ### method:1
17 | view实例的方式 Key有没有都可以 也不需要js监听事件 只需要复写onPullRelease即可以使用
18 | debug测试可以使用但是在release模式下会有收不到消息的情况,官方原因并不稳定
19 | ### method:2
20 | 原生广播的方式想rn发送数据 ### 因此Key必须有切唯一不重复 ### 需要rn端写事件监听 稳定暂时未发现bug
21 | 具体建议参考:[RNApp](https://github.com/wuyunqiang/RNApp)
22 | 
23 | 
24 | ```
25 |
31 | {this.renderView()}
32 |
33 |
34 |
35 |
36 | this.pullList = list}
41 | onEndReachedThreshold={20}
42 | onPullRelease={this.onPullRelease}
43 | onEndReached={this.loadMore}
44 | renderItem={this.renderRowView}
45 | getItemLayout={(data, index) => ({length:230, offset:230 * index, index})}
46 | numColumns={1}
47 | ItemSeparatorComponent={() => {
48 | return null;
49 | }}
50 | initialNumToRender={5}
51 | renderLoading = {()=>{return null;}}
52 | />
53 | ```
54 |
--------------------------------------------------------------------------------
/pull/PullLayout.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by wuyunqiang on 2018/1/16.
3 | */
4 | import React, { Component } from 'react';
5 | import {
6 | AppRegistry,
7 | Platform,
8 | StyleSheet,
9 | Text,
10 | View,
11 | ScrollView,
12 | Image,
13 | UIManager,
14 | TouchableOpacity,
15 | NativeModules,
16 | ImageBackground,
17 | DeviceEventEmitter,
18 | requireNativeComponent,
19 | } from 'react-native';
20 | const ReactNative = require('ReactNative');
21 | import PropTypes from 'prop-types';
22 | var PullLayout = requireNativeComponent('PullLayout', App);
23 | export default class App extends Component {
24 | constructor(props){
25 | super(props);
26 | }
27 |
28 | //数据获取后回调 刷新结束
29 | finishRefresh = (key)=>{
30 | Log("结束下拉"+key);
31 | UIManager.dispatchViewManagerCommand(ReactNative.findNodeHandle(this),
32 | UIManager.PullLayout.Commands.FinishRefresh,[key])
33 | };
34 |
35 | resolve = ()=>{
36 | this.finishRefresh(this.props.Key);
37 | };
38 |
39 | onPullRelease = ()=>{
40 | this.props.onPullRelease(this.resolve)
41 | };
42 |
43 | render() {
44 | console.log('PullLayout this.props',this.props)
45 | return (
46 | {this.pullLayout = pull}}
49 | style={[{flex: 1,backgroundColor:'white',},this.props.style]}
50 | EnableOverScrollDrag = {true}
51 | EnableOverScrollBounce = {false}
52 | DisableContentWhenRefresh = {false}
53 | onRefreshReleased={this.onPullRelease}
54 | {...this.props}
55 | >
56 |
57 | {this.props.children}
58 |
59 |
60 | )
61 | }
62 | }
63 |
64 | PullLayout.propTypes = {
65 | ...View.propTypes,
66 | Key:PropTypes.string.isRequired,//必须 否则监听回调可能无法被调用
67 | Method:PropTypes.number.isRequired,//使用哪种方式发送消息 1实例方式 2广播消息的方式 Key针对第二种方式
68 | onRefreshReleased:PropTypes.func,//网络请求加载数据
69 | EnableOverScrollDrag:PropTypes.bool,//设置是否启用越界回弹
70 | EnableOverScrollBounce:PropTypes.bool,//设置是否启用越界拖动(仿苹果效果)
71 | DragRate:PropTypes.number, //显示拖动高度/真实拖动高度(默认0.5,阻尼效果)
72 | HeaderMaxDragRate:PropTypes.number,//设置下拉最大高度和Header高度的比率(将会影响可以下拉的最大高度)
73 | HeaderTriggerRate:PropTypes.number,//设置 触发刷新距离 与 HeaderHieght 的比率
74 | ReboundDuration:PropTypes.number,//设置回弹动画时长
75 | EnableRefresh:PropTypes.bool,//是否启用下拉刷新(默认启用)
76 | EnableHeaderTranslationContent:PropTypes.bool,//设置是否启在下拉Header的同时下拉内容
77 | DisableContentWhenRefresh:PropTypes.bool,//设置是否开启在刷新时候禁止操作内容视图
78 | EnablePureScrollMode:PropTypes.bool,//设置是否开启纯滚动模式
79 | EnableNestedScroll:PropTypes.bool,//设置是会否启用嵌套滚动功能(默认关闭+智能开启)
80 | };
--------------------------------------------------------------------------------
/pull/LoadingSpinner.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from "react";
2 | import {Dimensions, ActivityIndicator,View, Text} from "react-native";
3 | const {width, height} = Dimensions.get('window');
4 | export default class LoadingSpinner extends Component {
5 |
6 | static defaultProps = {
7 | width: width,
8 | height: height,
9 | spinnerColor: 'white',
10 | textColor: 'white',
11 | text: '努力加载中...',
12 | backgroundColor:'transparent'
13 | };
14 |
15 | render() {
16 | if(this.props.type==='normal'){
17 | return (
18 |
19 |
20 |
21 | {this.props.text}
22 |
23 |
24 | )
25 | }else if(this.props.type==='bottom'){
26 | return (
27 |
28 |
29 | {this.props.text}
30 |
31 | )
32 | }else if(this.props.type==='allLoaded'){
33 | return (没有更多数据了)
34 | } else if(this.props.type==='home'){
35 | return (
36 |
37 | );
38 |
39 | }else{
40 | return (
41 |
52 |
58 |
59 | {this.props.text}
60 |
61 |
62 | );
63 | }
64 |
65 | }
66 | }
67 |
68 | const styles = {
69 | allLoaded:{
70 | marginLeft: SCALE(20),
71 | marginRight: SCALE(20),
72 | justifyContent:'center',
73 | alignItems:'center',
74 | height:SCALE(100),
75 | backgroundColor:Color.f3f3f3,
76 | },
77 | statusText:{
78 | backgroundColor:'transparent',
79 | fontSize:FONT(13),
80 | color:Color.C333333,
81 | }
82 | };
83 |
--------------------------------------------------------------------------------
/pull/PullView.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import {
5 | Platform,
6 | ScrollView,
7 | } from 'react-native';
8 | import Pullable from './Pullable';
9 |
10 | /**
11 | 支持android&ios可以下拉刷新的PullView组件
12 | Demo:
13 | import {PullView} from 'react-native-pullview';
14 |
15 | {}} topIndicatorHeight={60}
17 | >
18 |
19 | Demo2:
20 | topIndicatorRender(pulling, pullok, pullrelease) {
21 | return
22 |
23 | {pulling ? 下拉刷新2... : null}
24 | {pullok ? 松开刷新2...... : null}
25 | {pullrelease ? 玩命刷新中2...... : null}
26 | ;
27 | }
28 |
29 |
30 |
31 |
32 | Demo3:
33 |
34 | onRefresh() {
35 | this.setState({refreshing: true});
36 | return new Promise((resolve) => {
37 | setTimeout(() => {resolve()}, 9000);
38 | }).then(() => {
39 | this.setState({refreshing: false})
40 | })
41 | // setTimeout(() => {
42 | // this.setState({refreshing: false});
43 | // }, 3000);
44 | }
45 |
46 |
47 |
48 | */
49 |
50 | export default class extends Pullable {
51 | constructor(props) {
52 | super(props);
53 | this.state = {
54 | ...this.BaseState,
55 | };
56 | const defaultFlag = {pulling: false, pullok: false, pullrelease: false};
57 | this.setFlag(defaultFlag);
58 | Log("this.state",this.state)
59 | this.scrollTo = this.scrollTo.bind(this);
60 | this.scrollToEnd = this.scrollToEnd.bind(this);
61 | this.type='View';
62 | }
63 |
64 | scrollTo(...args) {
65 | this.scroll&&this.scroll.scrollTo(...args);
66 | }
67 |
68 | scrollToEnd(args) {
69 | this.scroll&&this.scroll.scrollTo(args);
70 | }
71 |
72 | onContentSizeChange = (contentWidth, contentHeight)=>{
73 | Log('contentWidth',contentWidth);
74 | Log('contentHeight',contentHeight);
75 | Log('Height',HEIGHT);
76 | this.type='View';
77 | if(contentHeight {this.scroll = c;}}
88 | refreshControl={refreshControl}
89 | onScroll={this.onScroll}
90 | scrollEnabled={Platform.OS==='android'?this.state.scrollEnabled:true}
91 | scrollEventThrottle={16}
92 | onContentSizeChange = {this.onContentSizeChange}
93 | {...this.props}
94 | >
95 | {this.props.children}
96 |
97 | );
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/pull/PullList.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by guzhenfu on 17/5/9.
3 | */
4 |
5 | import React, { Component } from 'react';
6 | import {
7 | InteractionManager,
8 | FlatList,
9 | Text,
10 | ActivityIndicator,
11 | View,
12 | Dimensions,
13 | TouchableHighlight,
14 | StyleSheet,
15 | Image,
16 | TouchableOpacity,
17 | NetInfo
18 | } from 'react-native';
19 | import Pullable from './Pullable';
20 | import LoadingSpinner from './LoadingSpinner';
21 | const LoadingState = 1; //初始loading页面
22 | const EmptyState = 2; //空页面
23 | const ErrorState = 3; //加载数据错误
24 | const ListState = 4; //正常加载
25 | const MoreState = 5; //正在加载更多
26 | const NoMoreState = 6; //没有更多了
27 | const NoMoreErrorState = 7; //加载更多出错
28 |
29 | export default class PullList extends Pullable {
30 |
31 | constructor(props) {
32 | super(props);
33 | this.state = {
34 | ...this.BaseState,
35 | data:[],
36 | };
37 | this.currentState = LoadingState;
38 | this.page = 0;
39 | this.type='List';
40 | }
41 |
42 | componentDidMount() {
43 | this.mounted = true;
44 | InteractionManager.runAfterInteractions(()=>{
45 | this.loadMore()
46 | // this.props.onPullRelease(this.resolveHandler);
47 | })
48 | }
49 |
50 | componentWillMount() {
51 | this.mounted = false;
52 | }
53 |
54 | setPage = (page) => {
55 | this.page = page;
56 | };
57 |
58 | getPage = () => {
59 | return this.page;
60 | };
61 |
62 | renderSeparatorView = ()=>{
63 | if(this.props.ItemSeparatorComponent){
64 | return this.props.ItemSeparatorComponent();
65 | }
66 | return ()
67 | };
68 |
69 | /**
70 | * 刷新
71 | */
72 | refresh = () => {
73 | Log('zhixngzheli');
74 | if (this.mounted) {
75 | this.setPage(1);
76 | this.props.onPullRelease&&this.props.onPullRelease(this.resolveHandler);
77 | }
78 | };
79 |
80 | getMetrics = (args)=> {
81 | this.scroll&&this.scroll.getMetrics(args);
82 | };
83 |
84 | scrollToOffset = (...args)=> {
85 | this.scroll&&this.scroll.scrollToOffset(...args);
86 | };
87 |
88 | scrollToEnd = (args)=> {
89 | this.scroll&&this.scroll.scrollToEnd(args);
90 | };
91 |
92 | /**
93 | * 对外提供API,设置列表数据
94 | */
95 | setData = (_data)=>{
96 | this.setPage(1);
97 | Log('下拉刷新设置数据 setData');
98 | if (_data.length == 0){
99 | this.currentState = EmptyState;
100 | }else{
101 | this.currentState = ListState;
102 | }
103 | this.setState({
104 | data: _data,
105 | })
106 |
107 | };
108 |
109 | /**
110 | * 对外提供API, loadMore 调用
111 | */
112 | addData = (_data)=>{
113 | if (_data.length == 0){
114 | this.currentState = NoMoreState;
115 |
116 | }else{
117 | this.currentState = MoreState;
118 | }
119 | this.setState({
120 | data: this.state.data.concat(_data),
121 | })
122 | };
123 |
124 |
125 |
126 | /**
127 | * 对外提供API, 出错重新加载数据
128 | */
129 | reloadData = ()=>{
130 | this.currentState = LoadingState;
131 | this.props.onPullRelease&&this.props.onPullRelease(this.resolveHandler);
132 | // this.forceUpdate();
133 | };
134 |
135 | /**
136 | * 对外提供API, 加载更多
137 | */
138 | resumeMoreDataFromError = ()=>{
139 | this.currentState = MoreState;
140 | this.page++;
141 | this.props.onEndReached&&this.props.onEndReached(this.getPage());
142 |
143 | Log('this.page',this.page)
144 | };
145 |
146 | /**
147 | * 加载loading页面
148 | * @returns {XML}
149 | * @private
150 | */
151 | _renderLoading = ()=> {
152 | return (
153 | )
154 | // return (
155 | //
157 | //
158 | //
159 | // );
160 | };
161 |
162 | /**
163 | * 加载 空页面
164 | */
165 | _renderEmpty = ()=>{
166 | Log('没有数据');
167 | return (
168 |
169 |
170 |
171 |
172 | 暂无数据
173 |
174 |
175 |
176 | )
177 | };
178 |
179 | /**
180 | * 加载 出错页
181 | */
182 | _renderError = ()=>{
183 | return (
184 |
185 |
186 |
187 |
188 | 网络无法连接,点击屏幕重试
189 |
190 |
191 |
192 | )
193 | };
194 |
195 | //加载更过
196 | loadMore = ()=>{
197 | if(this.currentState==NoMoreState){
198 | return;
199 | }
200 | this.page++;
201 | this.props.onEndReached&&this.props.onEndReached(this.getPage())
202 |
203 | };
204 |
205 | /**
206 | * 加载列表数据
207 | * @returns {XML}
208 | * @private
209 | */
210 | _renderList = ()=>{
211 | return (
212 | {this.scroll = c;}}
215 | onScroll={this.onScroll}
216 | scrollEnabled={this.state.scrollEnabled}
217 | refreshing={false}
218 | keyExtractor={(item, index) => {return index.toString()}}
219 | onEndReachedThreshold={0.5}
220 | data={this.state.data}
221 | ListFooterComponent={this._renderFoot}
222 | windowSize={10}
223 | updateCellsBatchingPeriod={1}
224 | maxToRenderPerBatch={10}
225 | disableVirtualization={false}
226 | {...this.props}
227 | ItemSeparatorComponent={this.renderSeparatorView}
228 | onEndReached = {this.loadMore}/>
229 | );
230 | };
231 |
232 | renderNoMore = ()=>{
233 | return (
234 | 没有更多数据
235 | )
236 | };
237 |
238 | renderMoreError = ()=>{
239 | return (
240 | {this.resumeMoreDataFromError()}}>
244 | 网络错误, 点击重新加载
245 |
246 | );
247 | };
248 |
249 | renderMore = ()=>{
250 | return (
256 |
257 | );
258 | };
259 |
260 | /**
261 | * 加载更多 UI渲染
262 | * @returns {XML}
263 | * @private
264 | */
265 | _renderFoot = ()=>{
266 | if (this.currentState === NoMoreState){
267 | return this.props.renderNoMore?this.props.renderNoMore():this.renderNoMore();
268 | }else if(this.currentState === NoMoreErrorState){
269 | return this.props.renderMoreError?this.props.renderMoreError():this.renderMoreError();
270 | }else if(this.currentState >= ListState){
271 | return this.props.renderMore?this.props.renderMore():this.renderMore();
272 | }else{
273 | return null;
274 | }
275 | };
276 |
277 | /**
278 | * 类似 render() 方法,具体在父类里面
279 | * @returns {*}
280 | */
281 | getScrollable = ()=> {
282 | if (this.currentState === LoadingState){
283 | return this.props.renderLoading?this.props.renderLoading():this._renderLoading();
284 | }else if(this.currentState === EmptyState){
285 | if(this.props.renderEmpty){
286 | return this.props.renderEmpty(this.reloadData)
287 | }else{
288 | return this._renderEmpty();
289 | }
290 | // return this.props.renderEmpty || this._renderEmpty;
291 | }else{
292 | this.type='List';
293 | return this._renderList()
294 | }
295 | }
296 | }
297 |
298 | const styles = StyleSheet.create({
299 | contain:{
300 | flex:1,
301 | justifyContent: 'center',
302 | alignItems: 'center',
303 | backgroundColor: Color.f3f3f3
304 | },
305 | footer:{
306 | height: 50,
307 | flex: 1,
308 | alignItems: 'center',
309 | justifyContent: 'center',
310 | borderTopWidth: 1,
311 | borderColor: "#CED0CE"
312 | }
313 | });
--------------------------------------------------------------------------------
/pull/index.js:
--------------------------------------------------------------------------------
1 | import React,{Component}from 'react';
2 | import {
3 | StyleSheet,
4 | Image,
5 | Text,
6 | Linking,
7 | View,
8 | Dimensions,
9 | Animated,
10 | Easing,
11 | ScrollView,
12 | PanResponder,
13 | ActivityIndicator,
14 | TouchableOpacity,
15 | StatusBar,
16 | Platform,
17 | NativeModules,
18 | ImageBackground,
19 | InteractionManager,
20 | TouchableHighlight,
21 | FlatList,
22 | DeviceEventEmitter
23 | } from 'react-native';
24 | import PullLayout from './PullLayout'
25 | import PullListView from './PullList'
26 | import PullView from './PullView'
27 |
28 | import LoadingSpinner from './LoadingSpinner';
29 | const LoadingState = 1; //初始loading页面
30 | const EmptyState = 2; //空页面
31 | const ErrorState = 3; //加载数据错误
32 | const ListState = 4; //正常加载
33 | const MoreState = 5; //正在加载更多
34 | const NoMoreState = 6; //没有更多了
35 | const NoMoreErrorState = 7; //加载更多出错
36 |
37 |
38 | /**
39 | * 使用方式
40 | * PullList =>flatlist
41 | * **/
42 | {/* this.pullList = list}*/}
46 | {/*topIndicatorRender={this.renderTopIndicator}*/}
47 | {/*topIndicatorHeight={header}*/}
48 | {/*onEndReachedThreshold={20}*/}
49 | {/*onPullRelease={this.onPullRelease}*/}
50 | {/*onEndReached={this.loadMore}*/}
51 | {/*renderItem={this.renderRowView}*/}
52 | {/*getItemLayout={(data, index) => ({length: SCALE(390), offset: SCALE(390) * index, index})}*/}
53 | {/*numColumns={1}*/}
54 | {/*ItemSeparatorComponent={() => {*/}
55 | {/*return null;*/}
56 | {/*}}*/}
57 | {/*initialNumToRender={5}*/}
58 | {/*renderLoading = {()=>{return null;}}*/}
59 | {/*/>*/}
60 |
61 | /**
62 | * PullScroll =>scrollview
63 | * **/
64 | {/**/}
69 | {/* super.navigate(...params)}/>*/}
74 | {/* super.navigate(...params)}/>*/}
75 | {/* super.navigate(...params)}/>*/}
76 | {/*
*/}
77 |
78 | /**
79 | * PullScroll => scrollview
80 | * PullList =>flatlist
81 | * Key 每一个实例唯一不能重复
82 | * Android_Native 是否使用android原生下拉刷新组件 true开启
83 | * ****/
84 | class Pull extends Component {
85 |
86 | constructor(){
87 | super();
88 | this.state = {
89 | data:[],
90 |
91 | };
92 | this.currentState=LoadingState;
93 | this.page = 0;
94 | this.type='List';
95 | }
96 |
97 | componentWillMount() {
98 | }
99 | componentDidMount() {
100 | if(Platform.OS=='android'&&this.props.Android_Native){
101 | this.subscription = DeviceEventEmitter.addListener(this.props.Key+"onRefreshReleased",this.refreshReleased);
102 | InteractionManager.runAfterInteractions(()=>{
103 | this.loadMore()
104 | })
105 | }
106 | // this.mounted = true;
107 | // this.setPage(1);
108 |
109 | }
110 |
111 | refreshReleased = ()=>{
112 | this.refresh();
113 | }
114 |
115 | componentWillUnmount() {
116 | if(Platform.OS=='android'&&this.props.Android_Native){
117 | this.pullLayout&&this.pullLayout.finishRefresh(this.props.Key);
118 | this.subscription&&this.subscription.remove();
119 | }
120 |
121 | }
122 |
123 |
124 | setPage = (page) => {
125 | if(Platform.OS=='ios'||!this.props.Android_Native){
126 | this.PullListView&&this.PullListView.setPage(page)
127 | return;
128 | }
129 | this.page = page;
130 | };
131 |
132 | getPage = () => {
133 | if(Platform.OS=='ios'||!this.props.Android_Native){
134 | return this.PullListView&&this.PullListView.getPage();
135 |
136 | }
137 | return this.page;
138 | };
139 |
140 | renderSeparatorView = ()=>{
141 |
142 | if(Platform.OS=='ios'||!this.props.Android_Native){
143 | return this.PullListView&&this.PullListView.renderSeparatorView();
144 |
145 | }
146 | if(this.props.ItemSeparatorComponent){
147 | return this.props.ItemSeparatorComponent();
148 | }
149 | return ()
150 | };
151 |
152 | /**
153 | * 刷新
154 | */
155 | refresh = () => {
156 | if(Platform.OS=='ios'||!this.props.Android_Native){
157 | this.PullListView&&this.PullListView.refresh();
158 | return;
159 | }
160 | Log('zhixngzheli');
161 | this.setPage(1);
162 | this.props.onPullRelease&&this.props.onPullRelease(this.resolveHandler);
163 | };
164 |
165 | resolveHandler = ()=>{
166 | Log('使用广播方式传送数据')
167 | this.pullLayout&&this.pullLayout.finishRefresh(this.props.Key);
168 | }
169 |
170 | getMetrics = (args)=> {
171 | if(Platform.OS=='ios'||!this.props.Android_Native){
172 | this.PullListView&&this.PullListView.getMetrics();
173 | return;
174 | }
175 | this.scroll&&this.scroll.getMetrics(args);
176 | };
177 |
178 | scrollToOffset = (...args)=> {
179 | if(Platform.OS=='ios'||!this.props.Android_Native){
180 | this.PullListView&&this.PullListView.scrollToOffset(...args);
181 | return;
182 | }
183 | this.scroll&&this.scroll.scrollToOffset(...args);
184 | };
185 |
186 | scrollToEnd = (args)=> {
187 | if(Platform.OS=='ios'||!this.props.Android_Native){
188 | this.PullListView&&this.PullListView.scrollToEnd(args);
189 | return;
190 | }
191 | this.scroll&&this.scroll.scrollToEnd(args);
192 | };
193 |
194 | /**
195 | * 对外提供API,设置列表数据
196 | */
197 | setData = (_data)=>{
198 | if(Platform.OS=='ios'||!this.props.Android_Native){
199 | this.PullListView&&this.PullListView.setData(_data)
200 | return;
201 | }
202 | this.setPage(1);
203 | Log('setData');
204 | if (_data.length == 0){
205 | this.currentState=EmptyState,
206 | this.setState({
207 | data: ['EmptyState'],//只有下拉刷新才会出现此状态
208 | });
209 | }else{
210 | this.currentState=ListState;
211 | this.setState({
212 | data: _data,
213 | });
214 | }
215 | };
216 |
217 | /**
218 | * 对外提供API, loadMore 调用
219 | */
220 | addData = (_data)=>{
221 | if(Platform.OS=='ios'||!this.props.Android_Native){
222 | this.PullListView&&this.PullListView.addData(_data)
223 | return;
224 | }
225 |
226 | let data = this.state.data;
227 | if(this.state.data[this.state.data.length-1]=='EmptyState'){
228 | data.pop();
229 | }
230 | if (_data.length == 0){
231 | this.currentState=NoMoreState;
232 | this.setState({
233 | data: data.concat(_data),
234 | })
235 | }else{
236 | this.currentState = MoreState;
237 | this.setState({
238 |
239 | data: data.concat(_data),
240 | });
241 | }
242 |
243 | };
244 |
245 |
246 |
247 | /**
248 | * 对外提供API, 出错重新加载数据
249 | */
250 | reloadData = ()=>{
251 | if(Platform.OS=='ios'||!this.props.Android_Native){
252 | this.PullListView&&this.PullListView.reloadData()
253 | return;
254 | }
255 | this.currentState=LoadingState;
256 | this.props.onPullRelease&&this.props.onPullRelease(this.resolveHandler);
257 | // this.forceUpdate();
258 | };
259 |
260 |
261 | //加载更多
262 | loadMore = ()=>{
263 | if(Platform.OS=='ios'||!this.props.Android_Native){
264 | this.PullListView&&this.PullListView.loadMore()
265 | return;
266 | }
267 | if(this.currentState==NoMoreState){
268 | return;
269 | }
270 | this.page++;
271 | this.props.onEndReached&&this.props.onEndReached(this.getPage());
272 |
273 | }
274 |
275 | /**
276 | * 对外提供API, 加载更多
277 | */
278 | resumeMoreDataFromError = ()=>{
279 | if(Platform.OS=='ios'||!this.props.Android_Native){
280 | this.PullListView&&this.PullListView.resumeMoreDataFromError()
281 | return;
282 | }
283 | this.currentState=MoreState;
284 | this.page++;
285 | Log('this.page',this.page)
286 | this.props.onEndReached&&this.props.onEndReached(this.getPage());
287 | };
288 |
289 | /**
290 | * 加载loading页面
291 | * @returns {XML}
292 | * @private
293 | */
294 | _renderLoading = ()=> {
295 | if(Platform.OS=='ios'||!this.props.Android_Native){
296 | return this.PullListView&&this.PullListView._renderLoading()
297 | }
298 | return (
299 | )
300 | // return (
301 | //
303 | //
304 | //
305 | // );
306 | };
307 |
308 | /**
309 | * 加载 空页面
310 | */
311 | _renderEmpty = ()=>{
312 | if(Platform.OS=='ios'||!this.props.Android_Native){
313 | return this.PullListView&&this.PullListView._renderEmpty()
314 | }
315 | Log('没有数据');
316 | return (
317 |
318 |
319 |
320 |
321 |
322 | 暂无数据
323 |
324 |
325 |
326 |
327 | )
328 | };
329 |
330 | /**
331 | * 加载 出错页
332 | */
333 | _renderError = ()=>{
334 | if(Platform.OS=='ios'||!this.props.Android_Native){
335 | return this.PullListView&&this.PullListView._renderError()
336 | }
337 | return (
338 |
339 |
340 |
341 |
342 | 网络无法连接,点击屏幕重试
343 |
344 |
345 |
346 | )
347 | };
348 |
349 | renderNoMore = ()=>{
350 | if(Platform.OS=='ios'||!this.props.Android_Native){
351 | return this.PullListView&&this.PullListView.renderNoMore()
352 | }
353 | return (
354 | 没有更多数据
355 | )
356 | };
357 |
358 | renderMoreError = ()=>{
359 | if(Platform.OS=='ios'||!this.props.Android_Native){
360 | return this.PullListView&&this.PullListView.renderMoreError()
361 | }
362 | return (
363 | {this.resumeMoreDataFromError()}}>
367 | 网络错误, 点击重新加载
368 |
369 | );
370 | };
371 |
372 | renderRowView = ({item, index, separators})=>{
373 | if (item === 'LoadingState'){
374 | return this._renderLoading();
375 | }else if(item === 'EmptyState'){
376 | if(this.props.renderEmpty){
377 | return this.props.renderEmpty(this.reloadData)
378 | }else{
379 | return this._renderEmpty();
380 | }
381 | }else{
382 | return this.props.renderItem&&this.props.renderItem({item, index, separators})
383 | }
384 | }
385 |
386 | renderMore = ()=>{
387 |
388 | if(Platform.OS=='ios'||!this.props.Android_Native){
389 | return this.PullListView&&this.PullListView.renderMore()
390 | }
391 | return (
397 |
398 | );
399 | };
400 |
401 |
402 | /**
403 | * 类似 render() 方法,具体在父类里面
404 | * @returns {*}
405 | */
406 | // getScrollable = ()=> {
407 | // if (this.currentState === LoadingState){
408 | // return this.props.renderLoading || this._renderLoading();
409 | // }else if(this.currentState === EmptyState){
410 | // if(this.props.renderEmpty){
411 | // return this.props.renderEmpty(this.reloadData)
412 | // }else{
413 | // return this._renderEmpty();
414 | // }
415 | // // return this.props.renderEmpty || this._renderEmpty;
416 | // }else{
417 | // this.type='List';
418 | // return this._renderList()
419 | // }
420 | // }
421 |
422 | /**
423 | * 加载更多 UI渲染
424 | * @returns {XML}
425 | * @private
426 | */
427 | _renderFoot = ()=>{
428 | if(Platform.OS=='ios'||!this.props.Android_Native){
429 | return this.PullListView&&this.PullListView._renderFoot()
430 | }
431 | Log('执行了这里ListFooterComponent')
432 | if (this.currentState === NoMoreState){
433 | return this.props.renderNoMore?this.props.renderNoMore():this.renderNoMore();
434 | }else if(this.currentState === NoMoreErrorState){
435 | return this.props.renderMoreError?this.props.renderMoreError():this.renderMoreError();
436 | }else if(this.currentState >= ListState){
437 | return this.props.renderMore?this.props.renderMore():this.renderMore();
438 | }else{
439 | return null;
440 | }
441 | };
442 |
443 | render(){
444 | if (Platform.OS == 'android' && this.props.Android_Native) {
445 | return ( {
448 | this.pullLayout = pull
449 | }}
450 | style={{flex: 1, backgroundColor: 'white',}}>
451 | {
454 | this.scroll = c;
455 | }}
456 | refreshing={false}
457 | keyExtractor={(item, index) => {
458 | return index.toString()
459 | }}
460 | onEndReachedThreshold={20}
461 | data={this.state.data}
462 | windowSize={10}
463 | initialNumToRender={5}
464 | updateCellsBatchingPeriod={10}
465 | maxToRenderPerBatch={10}
466 | {...this.props}
467 | renderItem={this.renderRowView}
468 | onEndReached={this.loadMore}
469 | ListFooterComponent={this._renderFoot}
470 | />
471 | )
472 | }
473 | return ( {
476 | this.PullListView = pull
477 | }}
478 | />)
479 | }
480 | }
481 |
482 | class PullScroll extends Component{
483 |
484 | componentDidMount() {
485 | if(Platform.OS=='android'){
486 | this.subscription = DeviceEventEmitter.addListener(this.props.Key+"onRefreshReleased",this.refreshReleased);
487 | }
488 | }
489 |
490 | refreshReleased = ()=>{
491 | this.refresh();
492 | }
493 |
494 | /**
495 | * 刷新
496 | */
497 | refresh = () => {
498 | this.props.onPullRelease&&this.props.onPullRelease(this.resolveHandler);
499 | };
500 |
501 | resolveHandler = ()=>{
502 | Log('使用广播方式传送数据')
503 | this.pullLayout&&this.pullLayout.finishRefresh(this.props.Key);
504 | }
505 |
506 | componentWillUnmount() {
507 | if(Platform.OS=='android'){
508 | this.pullLayout&&this.pullLayout.finishRefresh(this.props.Key);
509 | }
510 | this.subscription&&this.subscription.remove();
511 | }
512 |
513 | render(){
514 | if (Platform.OS == 'android' && this.props.Android_Native) {
515 | console.log('pull index this.props.children',this.props.children)
516 | return ( {
519 | this.pullLayout = pull
520 | }}
521 | style={{flex: 1, backgroundColor: 'white',}}
522 | {...this.props}>
523 |
524 | {this.props.children}
525 |
526 | )
527 | }
528 | return (
530 | {this.props.children}
531 | )
532 | }
533 |
534 | }
535 |
536 |
537 |
538 | const styles = StyleSheet.create({
539 | contain:{
540 | flex:1,
541 | width:WIDTH,
542 | height:HEIGHT,
543 | justifyContent: 'center',
544 | alignItems: 'center',
545 | backgroundColor: Color.f3f3f3
546 | },
547 | footer:{
548 | height: 50,
549 | flex: 1,
550 | alignItems: 'center',
551 | justifyContent: 'center',
552 | borderTopWidth: 1,
553 | borderColor: "#CED0CE"
554 | }
555 | });
556 |
557 | export {
558 | Pull as PullList,
559 | PullScroll
560 | };
561 |
562 |
563 |
564 |
--------------------------------------------------------------------------------
/pull/Pullable.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component } from 'react';
3 | import {
4 | View,
5 | Text,
6 | RefreshControl,
7 | PanResponder,
8 | Animated,
9 | Easing,
10 | Dimensions,
11 | ActivityIndicator,
12 | StyleSheet,
13 | Platform
14 | } from 'react-native';
15 | // const padding = 2; //scrollview与外面容器的距离
16 | const pullOkMargin = 100; //下拉到ok状态时topindicator距离顶部的距离
17 | const defaultDuration = 300;
18 | const defaultTopIndicatorHeight = 80; //顶部刷新指示器的高度
19 | const defaultFlag = {pulling: false, pullok: false, pullrelease: false};
20 | const flagPulling = {pulling: true, pullok: false, pullrelease: false};
21 | const flagPullok = {pulling: false, pullok: true, pullrelease: false};
22 | const flagPullrelease = {pulling: false, pullok: false, pullrelease: true};
23 | const isDownGesture = (x, y) => {
24 | return y > 0 && (y > Math.abs(x));
25 | };
26 | const isUpGesture = (x, y) => {
27 | return y < 0 && (Math.abs(x) < Math.abs(y));
28 | };
29 | const isVerticalGesture = (x, y) => {
30 | return (Math.abs(x) < Math.abs(y));
31 | };
32 |
33 |
34 | export default class extends Component {
35 |
36 | constructor(props) {
37 | super(props);
38 | this.pullable = this.props.refreshControl == null;
39 | this.defaultScrollEnabled = false; //定义onPull***属性时scrollEnabled为false
40 | this.topIndicatorHeight = this.props.topIndicatorHeight ? this.props.topIndicatorHeight : defaultTopIndicatorHeight;
41 | this.defaultXY = {x: 0, y: this.topIndicatorHeight * -1};
42 | this.pullOkMargin = this.props.pullOkMargin ? this.props.pullOkMargin : pullOkMargin;
43 | this.duration = this.props.duration ? this.props.duration : defaultDuration;
44 | this.BaseState = Object.assign({}, props, {
45 | arrowAngle: new Animated.Value(0),
46 | pullPan: new Animated.ValueXY(this.defaultXY),
47 | scrollEnabled: this.defaultScrollEnabled,
48 | flag: defaultFlag,
49 | height: 0,
50 | prArrowDeg:new Animated.Value(0),
51 | });
52 | // this.scrollEnabled = this.defaultScrollEnabled;
53 | this.gesturePosition = {x: 0, y: 0};
54 | this.onScroll = this.onScroll.bind(this);
55 | this.onLayout = this.onLayout.bind(this);
56 | this.BeginRefresh = this.BeginRefresh.bind(this);
57 | this.StopRefresh = this.StopRefresh.bind(this);
58 | this.isPullState = this.isPullState.bind(this);
59 | this.resetDefaultXYHandler = this.resetDefaultXYHandler.bind(this);
60 | this.resolveHandler = this.resolveHandler.bind(this);
61 | this.setFlag = this.setFlag.bind(this);
62 | this.renderTopIndicator = this.renderTopIndicator.bind(this);
63 | this.renderSpinner = this.renderSpinner.bind(this);
64 | this.defaultTopIndicatorRender = this.defaultTopIndicatorRender.bind(this);
65 | this.panResponder = PanResponder.create({
66 | onStartShouldSetPanResponder: this.onShouldSetPanResponder.bind(this),
67 | onMoveShouldSetPanResponder: this.onShouldSetPanResponder.bind(this),
68 | onPanResponderGrant: () => {},
69 | onPanResponderMove: this.onPanResponderMove.bind(this),
70 | onPanResponderRelease: this.onPanResponderRelease.bind(this),
71 | onPanResponderTerminate: this.onPanResponderRelease.bind(this),
72 | });
73 | this.IOS = (Platform.OS==='ios'?true:false);
74 | this.flag = defaultFlag;
75 | this.storyTimeKey = "story_time_key";
76 | this.base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAABQBAMAAAD8TNiNAAAAJ1BMVEUAAACqqqplZWVnZ2doaGhqampoaGhpaWlnZ2dmZmZlZWVmZmZnZ2duD78kAAAADHRSTlMAA6CYqZOlnI+Kg/B86E+1AAAAhklEQVQ4y+2LvQ3CQAxGLSHEBSg8AAX0jECTnhFosgcjZKr8StE3VHz5EkeRMkF0rzk/P58k9rgOW78j+TE99OoeKpEbCvcPVDJ0OvsJ9bQs6Jxs26h5HCrlr9w8vi8zHphfmI0fcvO/ZXJG8wDzcvDFO2Y/AJj9ADE7gXmlxFMIyVpJ7DECzC9J2EC2ECAAAAAASUVORK5CYII=';
77 | }
78 |
79 | onShouldSetPanResponder(e, gesture) {
80 | if(this.type==='View'){
81 | if (this.IOS&&(!this.pullable || isUpGesture(gesture.dx, gesture.dy)||!isVerticalGesture(gesture.dx, gesture.dy))) { //不使用pullable,或非向上 或向下手势不响应
82 | return false;
83 | }else{
84 | if (!this.IOS&&(!this.pullable || !isVerticalGesture(gesture.dx, gesture.dy))) { //不使用pullable,或非向上 或向下手势不响应
85 | return false;
86 | }
87 | }
88 | }else{
89 | if (!this.pullable || !isVerticalGesture(gesture.dx, gesture.dy)) { //不使用pullable,或非向上 或向下手势不响应
90 | return false;
91 | }
92 | }
93 |
94 | if (!this.state.scrollEnabled) {
95 | this.lastY = this.state.pullPan.y._value;
96 | return true;
97 | } else {
98 | return false;
99 | }
100 | }
101 |
102 | BeginRefresh(){
103 | Log('BeginRefresh');
104 | this.setFlag(flagPullrelease);
105 | this.state.pullPan.setValue({x: this.defaultXY.x, y: this.topIndicatorHeight});
106 |
107 | }
108 |
109 | StopRefresh(){
110 | Log('StopRefresh');
111 | this.resetDefaultXYHandler();
112 | }
113 |
114 | onPanResponderMove(e, gesture) {
115 | this.gesturePosition = {x: this.defaultXY.x, y: gesture.dy};
116 | if (isUpGesture(gesture.dx, gesture.dy)) { //向上滑动
117 | if(this.isPullState()) {
118 | this.resetDefaultXYHandler();
119 | } else if(this.props.onPushing && this.props.onPushing(this.gesturePosition)) {
120 | // do nothing, handling by this.props.onPushing
121 | } else {
122 | if(this.type==='View'){
123 | this.scroll&&this.scroll.scrollTo({x:0, y: gesture.dy * -1,animated:true});
124 | }else{
125 | this.scroll&&this.scroll.scrollToOffset({animated: true,
126 | offset: gesture.dy * -1});
127 | }
128 | }
129 | return;
130 | } else if (isDownGesture(gesture.dx, gesture.dy)) { //下拉
131 | this.state.pullPan.setValue({x: this.defaultXY.x, y: this.lastY + gesture.dy / 2});
132 | if (gesture.dy < this.topIndicatorHeight + this.pullOkMargin) { //正在下拉
133 | if (!this.flag.pulling) {
134 | this.props.onPulling && this.props.onPulling();
135 | }
136 | this.setFlag(flagPulling);
137 | } else { //下拉到位
138 | if (!this.state.pullok) {
139 | this.props.onPullOk && this.props.onPullOk();
140 | }
141 | this.setFlag(flagPullok);
142 | }
143 | }
144 | }
145 |
146 | onPanResponderRelease(e, gesture) {
147 | if (this.flag.pulling) { //没有下拉到位
148 | this.resetDefaultXYHandler(); //重置状态
149 | }
150 | if (this.flag.pullok) {
151 | if (!this.flag.pullrelease) {
152 | if (this.props.onPullRelease) {
153 | this.props.onPullRelease(this.resolveHandler);
154 | } else {
155 | setTimeout(() => {this.resetDefaultXYHandler()}, 3000);
156 | }
157 | }
158 | this.setFlag(flagPullrelease); //完成下拉,已松开
159 | Animated.timing(this.state.pullPan, {
160 | toValue: {x: 0, y: 0},
161 | easing: Easing.linear,
162 | duration: this.duration
163 | }).start();
164 | }
165 | }
166 |
167 | onScroll(e) {
168 | if (e.nativeEvent.contentOffset.y <= 0) {
169 | // Log('onScroll e.nativeEvent.contentOffset.y',e.nativeEvent.contentOffset.y);
170 | // this.scrollEnabled = this.defaultScrollEnabled;
171 | this.state.scrollEnabled===this.defaultScrollEnabled?"":this.setState({scrollEnabled: this.defaultScrollEnabled});
172 | } else if(!this.isPullState()) {
173 | // Log('onScroll e.nativeEvent.contentOffset.y',e.nativeEvent.contentOffset.y);
174 | // this.scrollEnabled = true;
175 | this.state.scrollEnabled?"":this.setState({scrollEnabled: true});
176 | }
177 | }
178 |
179 | isPullState() {
180 | return this.flag.pulling || this.flag.pullok || this.flag.pullrelease;
181 | }
182 |
183 | setFlag(flag) {
184 | if (this.flag != flag) {
185 | this.flag = flag;
186 | Log('设置inderTop',this.flag);
187 | this.renderTopIndicator();
188 | }
189 | }
190 |
191 | /** 数据加载完成后调用此方法进行重置归位
192 | */
193 | resolveHandler() {
194 | if (this.flag.pullrelease) { //仅触摸松开时才触发
195 | this.resetDefaultXYHandler();
196 | }
197 | }
198 |
199 | resetDefaultXYHandler() {
200 | this.flag = defaultFlag;
201 | Animated.timing(this.state.pullPan, {
202 | toValue: {x: 0, y: this.topIndicatorHeight * -1},
203 | easing: Easing.linear,
204 | duration: this.duration
205 | }).start();
206 | }
207 |
208 | componentWillUpdate(nextProps, nextState) {
209 | if (nextProps.isPullEnd && this.state.pullrelease) {
210 | this.resetDefaultXYHandler();
211 | }
212 | }
213 |
214 | onLayout(e) {
215 | if (this.state.width != e.nativeEvent.layout.width || this.state.height != e.nativeEvent.layout.height) {
216 | this.scrollContainer.setNativeProps({style: {width: e.nativeEvent.layout.width, height: e.nativeEvent.layout.height}});
217 | }
218 | }
219 |
220 | render() {
221 | // let refreshControl = this.props.refreshControl;
222 | return (
223 |
224 | {this.ani = c;}}
225 | style={[this.state.pullPan.getLayout()]}>
226 | {this.renderTopIndicator()}
227 | {this.scrollContainer = c;}}
228 | {...this.panResponder.panHandlers}
229 | style={{width: this.state.width, height: this.state.height}}>
230 | {this.getScrollable()}
231 |
232 |
233 |
234 | );
235 | }
236 |
237 | renderTopIndicator() {
238 | let { pulling, pullok, pullrelease } = this.flag;
239 | if (this.props.topIndicatorRender == null) {
240 | return this.defaultTopIndicatorRender(pulling, pullok, pullrelease, this.gesturePosition);
241 | } else {
242 | return this.props.topIndicatorRender(pulling, pullok, pullrelease, this.gesturePosition);
243 | }
244 | }
245 |
246 | /**
247 | 使用setNativeProps解决卡顿问题
248 | make changes directly to a component without using state/props to trigger a re-render of the entire subtree
249 | */
250 | // defaultTopIndicatorRender(pulling, pullok, pullrelease, gesturePosition) {
251 | //
252 | // this.transform = [{rotate:this.state.prArrowDeg.interpolate({
253 | // inputRange: [0,1],
254 | // outputRange: ['0deg', '-180deg']
255 | // })}];
256 | //
257 | // if (pulling) {
258 | // Animated.timing(this.state.prArrowDeg, {
259 | // toValue: 0,
260 | // duration: 100,
261 | // easing: Easing.inOut(Easing.quad)
262 | // }).start();
263 | // this.txtPulling && this.txtPulling.setNativeProps({style: styles.show});
264 | // this.txtPullok && this.txtPullok.setNativeProps({style: styles.hide});
265 | // this.txtPullrelease && this.txtPullrelease.setNativeProps({style: styles.hide});
266 | // } else if (pullok) {
267 | // Animated.timing(this.state.prArrowDeg, {
268 | // toValue: 1,
269 | // duration: 100,
270 | // easing: Easing.inOut(Easing.quad)
271 | // }).start();
272 | // this.txtPulling && this.txtPulling.setNativeProps({style: styles.hide});
273 | // this.txtPullok && this.txtPullok.setNativeProps({style: styles.show});
274 | // this.txtPullrelease && this.txtPullrelease.setNativeProps({style: styles.hide});
275 | // } else if (pullrelease) {
276 | // this.txtPulling && this.txtPulling.setNativeProps({style: styles.hide});
277 | // this.txtPullok && this.txtPullok.setNativeProps({style: styles.hide});
278 | // this.txtPullrelease && this.txtPullrelease.setNativeProps({style: styles.show});
279 | // }
280 | // return (
281 | //
282 | // {this.txtPulling = c;}} style={styles.hide}>
283 | //
286 | // {"下拉可以刷新"}
287 | //
288 | //
289 | // {this.txtPullok = c;}} style={styles.hide}>
290 | //
291 | //
294 | // {"释放立即刷新"}
295 | //
296 | //
297 | // {this.txtPullrelease = c;}} style={styles.hide}>
298 | //
299 | // {"刷新数据中..."}
300 | //
301 | //
302 | // 下拉刷新时间: {dateFormat(new Date().getTime(),'yyyy-MM-dd hh:mm')}
303 | //
304 | // );
305 | // }
306 |
307 | renderSpinner(status) {
308 | if (status === "txtPullrelease") {
309 | return (
310 |
311 | )
312 | }
313 | return (
314 |
328 | )
329 | }
330 |
331 | defaultTopIndicatorRender(pulling, pullok, pullrelease, gesturePosition) {
332 | Log('pulling, pullok, pullrelease',pulling, pullok, pullrelease);
333 | if (pulling) {
334 | Animated.timing(this.state.arrowAngle, {
335 | toValue: 0,
336 | duration: 50,
337 | easing: Easing.inOut(Easing.quad)
338 | }).start();
339 | this.txtPulling && this.txtPulling.setNativeProps({style: styles.show});
340 | this.txtPullok && this.txtPullok.setNativeProps({style: styles.hide});
341 | this.txtPullrelease && this.txtPullrelease.setNativeProps({style: styles.hide});
342 | } else if (pullok) {
343 | Animated.timing(this.state.arrowAngle, {
344 | toValue: 1,
345 | duration: 50,
346 | easing: Easing.inOut(Easing.quad)
347 | }).start();
348 | this.txtPulling && this.txtPulling.setNativeProps({style: styles.hide});
349 | this.txtPullok && this.txtPullok.setNativeProps({style: styles.show});
350 | this.txtPullrelease && this.txtPullrelease.setNativeProps({style: styles.hide});
351 | } else if (pullrelease) {
352 | Animated.timing(this.state.arrowAngle, {
353 | toValue: 1,
354 | duration: 50,
355 | easing: Easing.inOut(Easing.quad)
356 | }).start();
357 | this.txtPulling && this.txtPulling.setNativeProps({style: styles.hide});
358 | this.txtPullok && this.txtPullok.setNativeProps({style: styles.hide});
359 | this.txtPullrelease && this.txtPullrelease.setNativeProps({style: styles.show});
360 | }
361 | return (
362 | {this.PullAll = c;}} style={defaultHeaderStyles.header}>
363 | {this.txtPulling = c;}} style={styles.hide}>
364 | {this.renderSpinner("txtPulling")}
365 | {"下拉可以刷新"}
366 |
367 | {this.txtPullok = c;}} style={styles.hide}>
368 | {this.renderSpinner("txtPullok")}
369 | {"释放立即刷新"}
370 |
371 | {this.txtPullrelease = c;}} style={styles.hide}>
372 | {this.renderSpinner("txtPullrelease")}
373 | {"刷新数据中..."}
374 |
375 |
376 | );
377 | }
378 | }
379 |
380 | const dateFormat = function(dateTime, fmt) {
381 | let date = new Date(dateTime);
382 | fmt = fmt || 'yyyy-MM-dd';
383 | let o = {
384 | "M+": date.getMonth() + 1, //月份
385 | "d+": date.getDate(), //日
386 | "h+": date.getHours(), //小时
387 | "m+": date.getMinutes(), //分
388 | "s+": date.getSeconds(), //秒
389 | "q+": Math.floor((date.getMonth() + 3) / 3), //季度
390 | "S": date.getMilliseconds() //毫秒
391 | };
392 | if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
393 | for (let k in o)
394 | if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
395 | return fmt;
396 | }
397 |
398 |
399 |
400 | const styles = StyleSheet.create({
401 | wrap:{
402 | flex: 1,
403 | },
404 | headWrap:{
405 | height: defaultTopIndicatorHeight,
406 | justifyContent: 'center',
407 | alignItems: 'center',
408 | },
409 | hide: {
410 | position: 'absolute',
411 | left: 10000
412 | },
413 | show:{
414 | position: 'relative',
415 | left: 0,
416 | flexDirection: 'row',
417 | width: Dimensions.get('window').width,
418 | height: 40,
419 | justifyContent: 'center',
420 | alignItems: 'center',
421 | },
422 | arrow:{
423 | height: 30,
424 | width: 30,
425 | marginRight: 20,
426 | },
427 | arrowText:{
428 | marginLeft: 20,
429 | }
430 | });
431 | const defaultHeaderStyles = StyleSheet.create({
432 | header: {
433 | height: 80,
434 | alignItems: 'center',
435 | justifyContent: 'center'
436 | },
437 | status: {
438 | flexDirection: 'row',
439 | alignItems: 'center'
440 | },
441 | arrow: {
442 | width: 23,
443 | height: 23,
444 | marginRight: 10,
445 | opacity: 0.4
446 | },
447 | statusTitle: {
448 | fontSize: 13,
449 | color: '#333333'
450 | },
451 | date: {
452 | fontSize: 11,
453 | color: '#333333',
454 | marginTop: 5
455 | }
456 | });
--------------------------------------------------------------------------------