我正在开发一个水平滚动列表,它总是在每次滑动时滚动整个屏幕(即一个"页面")。当它位于第一页并且用户再次向右拉时,我需要刷新整个列表。
对于垂直ScrollView
,如果用户在视图位于其最顶端位置时向下拉,则会出现标准RefreshControl
。但对于横向版本,没有标准的解决方案。
我尝试添加PanResponder
来捕捉触摸和移动事件。请注意,我在我的应用程序中使用了VirtualizedList
。基本逻辑是:
View
(其宽度默认设置为0),其中包含ActivityIndicator
,并将此视图作为ListHeaderComponent
插入VirtualizedList
VirtualizedList
的{{1}}事件并继续保存最新的滚动位置onScroll
),然后使用ActivityIndicator设置隐藏视图的宽度以模拟RefreshControl行为但是,PanResponder
无法正确捕获所需的事件。大多数情况下,当我在PanResponder
的开头(在Android设备上)向右拉时,只会显示标准的命中边缘动画(显示渐变蓝色图层)。
通过添加一些日志,我发现偶尔会触发VirtualizedList
,但onPanResponderGrant
,onPanResponderMove
,onPanResponderRelease
或onPanResponderTerminate
都不会被触发
触发onPanResponderTerminationRequest
时,隐藏的onPanResponderGrant
偶尔会出现在个别地方。 "拉到右边"效果只会很快显示,并且总是立即停止,只显示隐藏视图的一小部分。
我猜底层ActivityIndicator
会窃取该事件并阻止我的代码响应。但即使我将ScrollView
设置为始终退回onPanResponderTerminationRequest
仍然无法提供帮助。即使我直接用false
替换VirtualizedList
也会产生相同的结果。
当ScrollView滚动到顶部时,有没有人知道如何正确捕捉触摸和移动事件?或者是否有另一种可行的方法来实现ScrollView
的横向版本?
RefreshControl
直接在import React from 'react'
import {
Text, View, ActivityIndicator, ScrollView, VirtualizedList,
Dimensions, PanResponder, Animated, Easing
} from 'react-native'
let DATA = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
class Test extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
refreshIndicatorWidth: new Animated.Value(0),
};
this.onScroll = evt => this._scrollPos = evt.nativeEvent.contentOffset.x;
this.onPullToRefresh = dist => Animated.timing(this.state.refreshIndicatorWidth, {
toValue: dist,
duration: 0,
easing: Easing.linear,
}).start();
this.resetRefreshStatus = () => {
Animated.timing(this.state.refreshIndicatorWidth, {
toValue: 0,
}).start();
};
const shouldRespondPan = ({dx, dy}) => (this._scrollPos||0) < 50 && dx > 10 && Math.abs(dx) > Math.abs(dy);
this._panResponder = PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => {
return shouldRespondPan(gestureState)
},
onMoveShouldSetPanResponderCapture: (evt, gestureState) => {
return shouldRespondPan(gestureState)
},
onPanResponderGrant: (evt, gestureState) => {
console.warn('----pull-Grant: ' + JSON.stringify(gestureState));
},
onPanResponderMove: (evt, gestureState) => {
console.warn('----pull-Move: ' + JSON.stringify(gestureState));
this.onPullToRefresh(gestureState.dx)
},
onPanResponderTerminationRequest: () => {
console.warn('----pull-TerminationRequest: ' + JSON.stringify(gestureState));
return false
},
onPanResponderRelease: (evt, gestureState) => {
console.warn('----pull-Release: ' + JSON.stringify(gestureState));
this.props.refreshMoodList();
},
onPanResponderTerminate: () => {
console.warn('----pull-Terminate: ' + JSON.stringify(gestureState));
this.resetRefreshStatus();
},
});
this.renderRow = this.renderRow.bind(this);
}
componentDidUpdate(prevProps, prevState, prevContext) {
console.warn('----updated: ' + JSON.stringify(this.props));
this.resetRefreshStatus();
}
renderRow({item, index}) {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center', width: Dimensions.get('window').width, height: '100%'}}>
<Text>{item}</Text>
</View>
);
}
render() {
/******* Test with a VirtualizedList *******/
return (
<VirtualizedList
{...this._panResponder.panHandlers}
data={DATA}
ListEmptyComponent={<View/>}
getItem={getItem}
getItemCount={getItemCount}
keyExtractor={keyExtractor}
renderItem={this.renderRow}
horizontal={true}
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
ListHeaderComponent={(
<Animated.View style={{flex: 1, height: '100%', width: this.state.refreshIndicatorWidth, justifyContent: 'center', alignItems: 'center'}}>
<ActivityIndicator size="large"/>
</Animated.View>
)}
onScroll={this.onScroll}
/>
);
/******* Test with a ScrollView *******/
return (
<View style={{height:Dimensions.get('window').height}}>
<ScrollView
{...this._panResponder.panHandlers}
horizontal={true}
>
<View style={{width: Dimensions.get('window').width * 2, height: 100, backgroundColor: 'green'}}>
<Text>123</Text>
</View>
</ScrollView>
</View>
);
}
}
const getItem = (data, index) => data[index];
const getItemCount = data => data.length;
const keyExtractor = (item, index) => 'R' + index;
export default Test;
中触发刷新事件(以及动画):这样,我仍然无法跟踪触摸移动。因此滚动窗格无法跟随触摸点。
将onPanResponderGrant
中的操作移至onPanResponderMove
并附加条件(例如添加我自己的&#34; grant&#34;条件判断):这种方式,我可以跟踪触摸动作,但由于我还没有得到真正的&#34;授予&#34;,我无法捕获onMoveShouldSetPanResponder
/ *Release
/ *Terminate
事件(即,我不知道触摸事件何时结束,因此实际上从未知道何时触发刷新事件!)
等待你的想法......
答案 0 :(得分:0)
您可以使用contentOffset
来确定用户是否在水平列表中“拉到刷新”。以下内容适用于FlatList
和ScrollView
:
onScroll={(e) => {
const xOffset = e.nativeEvent.contentOffset.x;
// logic goes here to handle a negative value
}}
scrollEventThrottle={16}
当xOffset达到某个值时,你可以做一些事情或设置一个标志。希望这能让你开始!