實現(xiàn)效果
1、上下滑動切換
2、 左滑動彈出列表,右滑動取消列表
3、加載更多、刷新

實現(xiàn)效果
思路
<View 手勢(上滑動、下滑動、左滑動(彈出RightView))>
<FlatList 禁止?jié)L動 renderItem的高和寬和屏幕一樣/>
</View>
<RightView 手勢(只判斷右滑動,用于返回)/>
通過滑動的x、y判斷是上下滑動還是左右滑動
1.上下滑動
滑動中:通過 flatList.scrollToOffset 設置flatList的滾動位置
滑動結束:通過判斷滑動的位置 || 滑動的速度 進行切換頁面
- 滑動距離超過屏幕的1/4
- 滑動結束時間 - 滑動開始時間 < 300 && 滑動距離超過 50
再通過flatList.scrollToIndex設置flatList的滾動位置
2.左滑動,顯示RightView.RightView position:'absolute' left: screenWidth
左滑動過程中setState left 的值,配合LayoutAnimation實現(xiàn)RightView彈出
3.下拉刷新,上拉加載
下拉刷新:第一頁的位置,下拉超過30,則顯示ListHeaderComponent
上拉加載:最后一頁的位置,上拉超過30,則顯示loadingComponent
RightView 手勢
右滑動:setState left 的值,配合LayoutAnimation實現(xiàn)RightView消失
具體代碼
index.,js
class ClassName extends React.Component {
constructor(props) {
super(props);
this.props.navigation.setOptions({ headerShown: false })
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true)
}
this.state = {
list: [1,2,3,4,5],
cityListLeft:width,
refreshing:false,
isLoading:false
}
this.startTime = 0 // 滾動開始時間
this.endTime = 0 // 滾動結束時間 ,用于計算滾動的速度(結束時間 - 開始時間,時間越短,滾動速度越快)
this.page = 0 // 當前頁
// 根據(jù)滑動的距離 或者 滑動的速度且滑動距離大于50,判斷是否切換
// 縱向滾動切換頁面條件一
this.minSlideDistance = 50 // 滾動時間符合要求時的最小滾動距離
this.maxSlideTime = 300 // 最大的滾動時間,
// 條件二
this.minSlideScaleY = 1 / 4 // 最小的滾動比例
// 橫向滾動顯示頁面
this.minSlideScaleX = 1 / 4 // 最小的拖動比例
this.isRefresh = false // 用于判斷是否處于下拉刷新中,減少下拉過程總多次setState
this.isLoadMore = false // 用于判斷是否處于上拉加載更多中,減少上拉過程總多次setState
// 初始化時定義,不要在render中定義
this.viewabilityConfig = {viewAreaCoveragePercentThreshold: 80}
this.panResponder = PanResponder.create({
// 要求成為響應者,防止彈出的頁面中按鈕不可點擊
onStartShouldSetPanResponder: (evt, gestureState) => {
if (Math.abs(gestureState.dx) > 10 || Math.abs(gestureState.dy) > 10){
return true
}
return false
},
onStartShouldSetPanResponderCapture: (evt, gestureState) => {
if (Math.abs(gestureState.dx) > 10 || Math.abs(gestureState.dy) > 10){
return true
}
return false
},
onMoveShouldSetPanResponder: (evt, gestureState) => {
if (Math.abs(gestureState.dx) > 10 || Math.abs(gestureState.dy) > 10){
return true
}
return false
},
onMoveShouldSetPanResponderCapture: (evt, gestureState) => {
if (Math.abs(gestureState.dx) > 10 || Math.abs(gestureState.dy) > 10){
return true
}
return false
},
onPanResponderGrant: (evt, gestureState) => {
// 滑動開始時間
this.startTime = evt.nativeEvent.timestamp
},
onPanResponderMove: (evt, gestureState) => {
let y = gestureState.dy
let x = gestureState.dx
if (Math.abs(x) > Math.abs(y)){
// 橫向滑動
if (Math.abs(x) > 30){
if (x > 0){
// 向右
if (this.state.cityListLeft == width){
}else {
this.setState({
cityListLeft:x
})
}
}else {
// 向左
if (this.state.cityListLeft == 0){
}else {
x = Math.abs(x)
this.setState({
cityListLeft:width - x
})
}
}
}
}else {
// 縱向滑動
if (Math.abs(y) > 30){
if (y > 0) {
// 第一頁,下拉加載更多
if (this.page == 0){
if (!this.isRefresh){
this.setState({
refreshing:true,
isLoading:true
})
this.isRefresh = true
// 請求接口
this.onRefresh()
}
this.flatList.scrollToOffset({animated: false,offset: -y})
}else {
this.flatList.scrollToOffset({animated: false,offset: this.page*height-y})
}
} else{
y = Math.abs(y)
if (this.page == this.state.list.length - 1){
// 最后一頁,上拉刷新
if (!this.isLoadMore){
this.setState({
isLoading:true,
})
this.isLoadMore = true
this.onEndReached()
}
}
this.flatList.scrollToOffset({animated: false, offset: this.page * height + y})
}
}
}
},
onPanResponderRelease: (evt, gestureState) => {
this.endTime = evt.nativeEvent.timestamp
let y = gestureState.dy
let x = gestureState.dx
if (Math.abs(x) > Math.abs(y)){
// 縱向重置狀態(tài)
this.flatList.scrollToIndex({ animated: true, index: this.page });
// 橫向滑動
if (x > 0){
if (this.state.cityListLeft == width){
}else {
if (x > width * this.minSlideScaleX){
this.setState({
cityListLeft:width
})
}else {
this.setState({
cityListLeft:0
})
}
LayoutAnimation.easeInEaseOut();
}
}else {
if (this.state.cityListLeft == 0){
}else {
if (Math.abs(x) > width * this.minSlideScaleX){
this.setState({
cityListLeft:0
})
}else {
this.setState({
cityListLeft:width
})
}
LayoutAnimation.easeInEaseOut();
}
}
}else {
// 縱向滑動
// 橫向重置狀態(tài)
this.setState({
cityListLeft:width
})
if (y > 0) {
// 向上
if (y > height * this.minSlideScaleY || (this.endTime - this.startTime < this.maxSlideTime && y > this.minSlideDistance)) {
if(this.page != 0){
this.page -= 1
}
}
this.flatList.scrollToIndex({ animated: true, index: this.page });
} else if (y < 0) {
// 向下
y = Math.abs(y)
if (y > height * this.minSlideScaleY || (this.endTime - this.startTime < this.maxSlideTime && y > this.minSlideDistance)) {
if(this.state.list.length != this.page + 1){
this.page += 1
}
}
this.flatList.scrollToIndex({ animated: true, index: this.page });
}
}
},
onPanResponderTerminate: (evt, gestureState) => {
},
onShouldBlockNativeResponder: (evt, gestureState) => {
return false;
},
});
this.cityPanResponder = PanResponder.create({
// 要求成為響應者:
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
},
onPanResponderMove: (evt, gestureState) => {
let x = gestureState.dx
// 橫向滑動
if (Math.abs(x) > 30){
if (x > 0){
// 向右
this.setState({
cityListLeft:x
})
}
}
},
onPanResponderRelease: (evt, gestureState) => {
this.endTime = evt.nativeEvent.timestamp
let x = gestureState.dx
// 橫向滑動
if (x > 0){
console.log('向右');
if (x > width * this.minSlideScaleX){
this.setState({
cityListLeft:width
})
}else {
this.setState({
cityListLeft:0
})
}
LayoutAnimation.easeInEaseOut();
}
},
onPanResponderTerminate: (evt, gestureState) => {
},
onShouldBlockNativeResponder: (evt, gestureState) => {
return false;
},
});
}
componentWillUnmount() {
}
componentDidMount(){
}
onEndReached=()=>{
console.log('onEndReached');
setTimeout(() => {
this.setState({
isLoading:false,
})
this.isLoadMore = false
}, 1500);
}
onRefresh=()=>{
// 請求接口數(shù)據(jù),刷新
setTimeout(() => {
this.setState({
refreshing:false,
isLoading:false
})
this.isRefresh = false
}, 1500);
}
// 不要在render中定義
onViewableItemsChanged=(info)=>{
console.log('infoinfoinfoinfo===',info);
if(info.viewableItems.length > 0){
// 顯示了新的item
}
}
render() {
return Render.render.call(this);
}
}
const mapStateToProps = state => {
return {
userInfo: state.userInfo,
globalInfo: state.globalInfo,
}
};
const mapDispatchToProps = dispatch => ({
getUserInfo: userInfo => dispatch({type: ActionTypes.GET_USERINFO, payload: {userInfo}}),
loginSuccess: globalInfo => dispatch({type: ActionTypes.LOGIN_SUCCESS, payload: {globalInfo}}),
setWalletMine: wallet => dispatch({type: ActionTypes.SET_WALLET_MINE, payload: wallet}),
});
export default connect(mapStateToProps, mapDispatchToProps)(FishpondList);
render.js
<View style={styles.mainView} {...this.panResponder.panHandlers}>
<FlatList ref={ref=>this.flatList = ref}
data={this.state.list}
renderItem={({ item, index }) => {
return <View key={index}>
<View style={[styles.item,{
backgroundColor:`rgb(${Math.random() * 255},${Math.random() * 255},${Math.random() * 255})`
}]}>
<Text style={{color:'#fff',fontSize:70}}>{index+1}</Text>
</View>
</View>
}}
ListHeaderComponent={this.state.refreshing ?<ListHeaderComponent/> : <View/>}
getItemLayout={(data, index) => (
{ length: styles.item.height, offset: styles.item.height * index, index }
)}
scrollEnabled={false}
onViewableItemsChanged={this.onViewableItemsChanged}
viewabilityConfig={this.viewabilityConfig}
showsVerticalScrollIndicator={false}
/>
</View>
<RightView panResponder={this.cityPanResponder} left={this.state.cityListLeft}/>