onPanResponderRelease没有被触发

时间:2016-08-28 01:20:29

标签: react-native

我正在尝试在View上使用PanResponder。 onStartShouldSetPanResponderonMoveShouldSetPanResponderonPanResponderMoveonPanResponderGrantonPanResponderRelease根本没有被触发。我的反应和反应原生版本是:

"react": "^15.2.1",
"react-native": "^0.30.0",

以下是代码

'use strict'
import React from 'react'
const Icon = require('react-native-vector-icons/Ionicons')
let THUMB_URLS = require('../Statics/ListingsData.js')
let SidePanelComponent = require('./common/SidePanel.js')
let RecentSearches = require('./Views/RecentSearches/RecentSearches.js')
let TimerMixin = require('react-timer-mixin')
const Loader = require('./common/LoadingState.js')
import { getImageURL, getUserImageURL } from './_helpers/images'

const config = require('../config')
import GoogleAnalytics from 'react-native-google-analytics-bridge'
GoogleAnalytics.setTrackerId(config.google_analytics_id)

const windowSize = require('Dimensions').get('window')
const deviceWidth = windowSize.width
const deviceHeight = windowSize.height

import {
  Image,
  Text,
  View,
  TouchableOpacity,
  TouchableWithoutFeedback,
  ScrollView,
  StyleSheet,
  Platform,
  Animated,
  PanResponder
} from 'react-native'

let LISTINGS = []

const ListingsViewComponent = React.createClass({
  mixins: [TimerMixin],

  getInitialState: function () {
    return {
      listings: [],
      dataSource: [],
      showSearchIcon: false,
      showSidePanel: false,
      photo: {},
      componentloading: true,
      showHeartIcon: [],
      startX: 0,
      startY: 0,
      showWishlistMenu: false,
      wishlistCurrentY: 0,
      showNewWishlistTextInput: false,
      currentRowdata: {},
      wishlistOptions: [],
      showrecentsearches: false,
      isDataLoading: true,
      scrolling: false,
      _listViewDirtyPressEnabled: true,
      scrollAnimationEnd: false,
      scrollStates: [],
      goingtonextview: false,
      heroImageContainerHeight: deviceWidth,
      searchbar: new Animated.ValueXY()
    }
  },

  _panListingsResponder: {},

  componentWillMount: function () {
    this._panListingsResponder = PanResponder.create({
      onStartShouldSetPanResponder: (e, g) => {
        this.setState({
          startX: e.nativeEvent.pageX,
          startY: e.nativeEvent.pageY
        })
      },
      onStartShouldSetPanResponderCapture: (e, g) => {
      },
      onMoveShouldSetPanResponder: (e, g) => {
        this.setState({
          heroImageContainerHeight: deviceWidth - (e.nativeEvent.pageY - this.state.startY)
        })
      },
      onMoveShouldSetPanResponderCapture: (e, g) => {},
      onPanResponderGrant: (e, g) => {},
      onPanResponderMove: (e, g) => {
        this.setState({
          heroImageContainerHeight: deviceWidth - (e.nativeEvent.pageY - this.state.startY)
        })
      },
      onPanResponderTerminationRequest: (e, g) => {
        console.log('onPanResponderTerminationRequest', e.nativeEvent)
        return false
      },
      onPanResponderRelease: (e, g) => {
        console.log('_onResponderRelease', e.nativeEvent)
      },
      onPanResponderTerminate: (e, g) => {
        console.log('onPanResponderTerminate', e.nativeEvent)
      },
      onShouldBlockNativeResponder: (e, g) => true
    })

    let listingsendpoint = 'http://faithstay-staging.herokuapp.com/api/listings'
    this.setState({
      isDataLoading: true
    })

    fetch(listingsendpoint)
      .then((response) => response.json())
      .then((listingsData) => {
        const listings = listingsData
        LISTINGS = []
        LISTINGS.push(THUMB_URLS[0])
        LISTINGS.push(THUMB_URLS[1])
        LISTINGS.push(THUMB_URLS[2])

        listings.map((listing) => {
          LISTINGS.push(listing)
        })

        this.setState({
          isDataLoading: false,
          listings: LISTINGS
        })
      })
      .catch((error) => {
        console.warn(error)
      })
  },

  componentDidMount: function () {
    GoogleAnalytics.trackScreenView('Faithstay-Listings-Page')
  },

  _showSidePanel: function () {
    this.setState({
      showSidePanel: true
    })
  },

  _closeSidePanel: function () {
    this.setState({
      showSidePanel: false
    })
  },

  _showRecentSearches: function () {
    this.setState({
      showrecentsearches: true
    })
  },

  _closeRecentSearches: function () {
    this.setState({
      showrecentsearches: false
    })
  },

  componentWillReceiveProps: function () {
    this.setState({
      goingtonextview: false
    })
  },

  getSearchBarStyle: function () {
    return [
      styles.searchbar, {
        top: this.state.heroImageContainerHeight
      }
    ]
  },

  render: function () {
    let sidePanelViewContainer
    if (this.state.showSidePanel) {
      sidePanelViewContainer = (<SidePanelComponent {...this.props} imageuri={this.state.photo} onClose={this._closeSidePanel} />)
    }

    let searchIconContainer = <Animated.View style={this.getSearchBarStyle()}>
      <TouchableOpacity style={styles.searchBarInner} onPress={this._showRecentSearches}>
        <Text style={styles.searchtext}>
          {'Where do you want to go?'}
        </Text>
        <Icon
          name={'ios-search'}
          size={30}
          color={'#cfcfcf'}
          style={styles.searchicon}
        />
      </TouchableOpacity>
    </Animated.View>

    if (!this.state.showrecentsearches) {
      if (this.state.isDataLoading) {
        return (<Loader />)
      } else {
        return (
          <View style={styles.container} {...this._panListingsResponder.panHandlers}>
            <View style={[styles.heroImageContainer, { height: this.state.heroImageContainerHeight }]}>
              <Image source={{uri: 'https://faithstay-statics.imgix.net/images/homepage_carousel_4.jpg'}} style={[styles.heroImage, { height: this.state.heroImageContainerHeight }]} />
              <View style={[styles.scrimLayer, { height: this.state.heroImageContainerHeight }]} />
              <View style={styles.logoContainer}>
                <Image source={require('../Statics/images/anchor_3x.png')} style={styles.logoImage} />
                <Text style={styles.logoText}>{'FaithStay'}</Text>
              </View>
              <View style={styles.horizontalDivider} />
              <View style={styles.betaVersionContainer}>
                <Text style={styles.betaVersionText}>{'Beta Version'}</Text>
              </View>
              <View style={[styles.pageTitleContainer, {top: this.state.heroImageContainerHeight - 85}]}>
                <Text style={styles.pageTitle}>{'Home'}</Text>
              </View>
              <View style={[styles.movableScrim, {backgroundColor: `rgba(0, 0, 0, ${(deviceWidth - this.state.heroImageContainerHeight) / deviceWidth})`}]} />
            </View>
            {searchIconContainer}
            <ScrollView style={styles.listView}>
              {this.getListingsView()}
            </ScrollView>
            {sidePanelViewContainer}
          </View>
        )
      }
    }
    return (<RecentSearches {...this.props} closeRecentSearches={this._closeRecentSearches} />)
  },

  _gotoUserProfilePage: function (user) {
    this.props.navigator.push({
      id: 15,
      passProps: {
        user
      }
    })
  },

  getListingsView: function () {
    let listings = this.state.listings
    const listingsArray = []
    listings.map((listing, i) => {
      let currentlisting = listing
      let type = currentlisting.type

      if (type !== 'NOT_A_LISTING') {
        let imgSource = {
          uri: getImageURL(currentlisting.images[0])
        }

        let profileimg = {
          uri: getUserImageURL(currentlisting.host)
        }

        let title = currentlisting.title
        let reviews = '18'
        let address_values = currentlisting.google_place.formatted_address ? currentlisting.google_place.formatted_address.split(',') : []
        let listing_address = {}

        if (address_values.length > 0) {
          listing_address = {
            country: address_values[address_values.length - 1].trim(),
            state: address_values[address_values.length - 2].trim(),
            city: address_values[address_values.length - 3].trim()
          }
        }

        let city = listing_address.city + ', ' + listing_address.state
        let baseprice = currentlisting.base_price ? '$' + currentlisting.base_price : '0'

        listingsArray.push(<View>
          <TouchableWithoutFeedback onPress={() => this._pressRow(currentlisting)}>
              <View>
                <View style={styles.row}>
                  <Image style={styles.thumb} source={imgSource} >
                    <View style={styles.priceconatiner}>
                      <Text style={styles.pricetext}>{baseprice}</Text>
                    </View>
                  </Image>
                </View>
                <TouchableOpacity style={styles.profileImgContainer} onPress={() => this._gotoUserProfilePage(currentlisting.host)}>
                  <Image style={styles.profileimg} source={profileimg} />
                </TouchableOpacity>
                <View style={styles.listingtextcontainer}>
                  <Text style={styles.listingtexttitle}>{title}</Text>
                  <Text style={styles.listingtexttdescription}>{'Entire Home' + ' - ' + reviews + ' Reviews' + ' - ' + city}</Text>
                </View>
            </View>
          </TouchableWithoutFeedback>
        </View>)
      } else {
        let listing_title = listing.title
        let listing_description = listing.description
        let imageuri = listing.image;
        listingsArray.push(<View><TouchableWithoutFeedback onPress={() => this._pressNonListingRow(currentlisting)}>
            <View>
                <View style={styles.rowNotListing}>
                  <Image style={styles.thumbNotListing} source={{uri: imageuri}}>
                    <View style={styles.thumbNotListing, {position: 'absolute', left:0, top: 0, right:0, bottom:0, backgroundColor: 'rgba(0,0,0,0.2)'}} >
                    </View>
                    <View style={styles.thumbNotListingSubContainer}>
                      <Text style={styles.listingtitle_notlisting}>{listing_title}</Text>
                      <Text style={styles.listingdescription_notlisting}>{listing_description}</Text>
                    </View>
                  </Image>
                </View>
            </View>
          </TouchableWithoutFeedback>
        </View>)
      }
    })
    return listingsArray
  },

  _pressRow: function (listing) {
    this.props.navigator.push({
      id: 4,
      passProps: {
        listingdata: listing
      }
    })
  },

  _pressNonListingRow: function (listing) {
    this.props.navigator.push({
      id: 9,
      passProps: {
        filterData: listing
      }
    })
  }
})

const paddingHorizontal = 15
const paddingVertical = 10
const distanceBetweenIcons = (deviceWidth - 115) / 3
const statusBarHeight = (Platform.OS === 'ios') ? 20 : 0

const isAndroid = Platform.OS === 'android'

const styles = StyleSheet.create({
  listView: {
    height: deviceHeight - 70,
    top: (Platform.OS === 'ios') ? 40 : 0,
    left: 0
  },
  scrimLayer: {
    position: 'absolute',
    top: 0,
    left: 0,
    width: deviceWidth,
    height: deviceWidth,
    backgroundColor: 'rgba(0, 0, 0, 0.2)'
  },
  movableScrim: {
    position: 'absolute',
    top: 0,
    left: 0,
    width: deviceWidth,
    height: deviceWidth
  },
  container: {
    flex: 1,
    paddingTop: statusBarHeight,
    width: deviceWidth,
    height: deviceHeight
  },

  row: {
    flexDirection: 'row',
    justifyContent: 'center',
    backgroundColor: '#f5f5f5',
    width: deviceWidth,
    height: deviceHeight / 2
  },

  separator: {
    height: 1,
    backgroundColor: '#CCCCCC'
  },

  thumb: {
    width: deviceWidth,
    height: deviceHeight / 2 - 80
  },

  thumbNotListing: {
    width: deviceWidth,
    height: deviceHeight / 2,
    justifyContent: 'center'
  },

  thumbNotListingSubContainer: {
    alignSelf: 'center',
    justifyContent: 'center'
  },

  listingtitle_notlisting: {
    textAlign: 'center',
    alignSelf: 'center',
    fontSize: 24,
    fontWeight: 'bold',
    color: '#ffffff'
  },

  listingdescription_notlisting: {
    textAlign: 'center',
    alignSelf: 'center',
    fontSize: 16,
    marginTop: 10,
    color: '#ffffff'
  },

  text: {
    flex: 1,
  },

  tabbar: {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    width: deviceWidth,
    height: 49,
    backgroundColor: '#f5f5f5',
    justifyContent: 'space-between',
    borderTopWidth: 1,
    borderTopColor: '#dce0e0'
  },

  searchbar: {
    width: deviceWidth - 30,
    height: 50,
    left: 15,
    top: deviceWidth - 5,
    position: 'absolute',
    justifyContent: 'center',
    backgroundColor: '#f5f5f5',
    shadowOpacity: 0.5
  },
  searchBarInner: {
    width: deviceWidth - 30,
    height: 50,
    justifyContent: 'center',
    backgroundColor: '#f5f5f5',
    shadowOpacity: 0.5
  },
  searchonlyicon: {
    width: 50,
    height: 50,
    borderRadius: 25,
    left: 20,
    top: 40,
    position: 'absolute',
    justifyContent: 'center',
    backgroundColor: '#f5f5f5',
    shadowOpacity: 0.5
  },
  searchtext: {
    width: 160,
    position: 'absolute',
    fontSize: 15,
    color: '#565a5c',
    left: (deviceWidth - 30) / 2 - 80,
    top: 15,
    fontFamily: 'RobotoCondensed-Regular'
  },
  searchicon: {
    width: 30,
    height: 30,
    position: 'absolute',
    top: 8,
    left: 12
  },
  homeicon: {
    width: 30,
    height: 30,
    position: 'absolute',
    top: paddingVertical - 2,
    left: paddingHorizontal,
    justifyContent: 'center',
  },
  hearticon: {
    width: 40,
    height: 30,
    position: 'absolute',
    top: paddingVertical,
    justifyContent: 'center',
    left: distanceBetweenIcons
  },
  emailicon: {
    width: 45,
    height: 30,
    position: 'absolute',
    top: paddingVertical,
    justifyContent: 'center',
    left: 2 * distanceBetweenIcons
  },
  bagicon: {
    width: 35,
    height: 20,
    position: 'absolute',
    top: paddingVertical + 6,
    justifyContent: 'center',
    left: 3 * distanceBetweenIcons
  },
  personicon: {
    width: 30,
    height: 30,
    position: 'absolute',
    top: paddingVertical,
    justifyContent: 'center',
    right: paddingHorizontal
  },
  priceconatiner: {
    position: 'absolute',
    top: deviceHeight / 2 - 150,
    left: 0,
    width: 60,
    height: 40,
    backgroundColor: 'rgba(60,63,64,0.9)',
    justifyContent: 'center'
  },
  pricetext: {
    fontSize: 20,
    color: '#fff',
    fontWeight: 'bold',
    textAlign: 'center',
    width: 60,
    fontFamily: 'HelveticaNeue'
  },
  profileImgContainer: {
    position: 'absolute',
    top: deviceHeight / 2 - 108,
    right: isAndroid ? 0 : 20,  // NOTE: add to width, vs pushing it with position values
    width: isAndroid ? 70 : 50, // NOTE: on android, the view must be as big as the image, otherwise the image will be cut off
    height: 50,
    paddingLeft: paddingHorizontal,
    justifyContent: 'center'
  },
  profileimg: {
    width: 50,
    height: 50,
    borderRadius: 25
  },
  listingtextcontainer: {
    position: 'absolute',
    top: deviceHeight / 2 - 70,
    left: paddingHorizontal,
    justifyContent: 'space-between',
    height: 50
  },
  listingtexttitle: {
    paddingTop: 5,
    fontSize: 16,
    fontFamily: 'HelveticaNeue',
    color: '#565a5c',
    fontWeight: 'bold'
  },
  listingtexttdescription: {
    fontSize: 14,
    fontFamily: 'HelveticaNeue',
    color: '#82888a',
    paddingBottom: 5
  },
  wishlistIcon: {
    position: 'absolute',
    right: 20,
    top: 20
  },
  hearticonwishlist: {
    width: 30,
    height: 30
  },
  wishlistScrollView: {
    position: 'absolute',
    right: 20,
    width: deviceWidth - 60,
    height: 80,
    backgroundColor: '#fff'
  },
  scrollRow: {
    width: 180,
    height: 40,
    justifyContent: 'center',
    padding: 5,
    borderBottomWidth: 1,
    borderBottomColor: '#f5f5f5'
  },
  wishlistScrollViewContainer: {
    position: 'absolute',
    top: 0,
    bottom: 0,
    left: 0,
    width: deviceWidth,
    height: deviceHeight,
    backgroundColor: 'rgba(255,255,255,0.1)'
  },

  touchableScrollViewContainer: {
    width: deviceWidth,
    height: deviceHeight,
    position: 'absolute',
    top: 0,
    bottom: 0,
    left: 0
  },

  fontWishlistScroller: {
    color: '#565a5c',
    fontSize: 14
  },

  rowNotListing: {
    flexDirection: 'row',
    justifyContent: 'center',
    width: deviceWidth,
    height: deviceHeight / 2
  },
  heroImageContainer: {
    width: deviceWidth,
    height: deviceWidth
  },
  heroImage: {
    width: deviceWidth,
    height: deviceWidth
  },
  logoContainer: {
    width: 120,
    position: 'absolute',
    left: (deviceWidth / 2) - 60,
    top: 19,
    flexDirection: 'row',
    justifyContent: 'center',
    backgroundColor: 'transparent'
  },
  logoImage: {
    width: 18,
    height: 30,
    top: 3
  },
  logoText: {
    fontFamily: 'RobotoCondensed-Regular',
    fontSize: 25,
    fontWeight: '400',
    textAlign: 'center',
    color: '#fffff0',
    marginLeft: 7.7
  },
  horizontalDivider: {
    width: 32,
    position: 'absolute',
    left: deviceWidth / 2 - 16,
    top: 59,
    borderBottomWidth: 1,
    borderColor: '#ffffff'
  },
  betaVersionContainer: {
    width: 120,
    position: 'absolute',
    left: deviceWidth / 2 - 60,
    top: 79,
    justifyContent: 'center',
    backgroundColor: 'transparent'
  },
  betaVersionText: {
    fontFamily: 'RobotoCondensed-Regular',
    fontSize: 14,
    fontStyle: 'italic',
    fontWeight: '300',
    textAlign: 'center',
    color: '#ffffff',
    alignSelf: 'center'
  },
  pageTitleContainer: {
    position: 'absolute',
    top: deviceWidth - 85,
    left: 20,
    backgroundColor: 'transparent'
  },
  pageTitle: {
    fontSize: 34,
    fontFamily: 'RobotoCondensed-Bold',
    color: '#ffffff'
  }
})

module.exports = ListingsViewComponent

2 个答案:

答案 0 :(得分:1)

通过使用onPanResponderEnd而不是onPanResponderRelease使它正常工作。

此外,如果我们仍然想使用onPanResponderRelease,则应通过以下方式允许终止请求:

onPanResponderTerminationRequest: () => true

答案 1 :(得分:0)

您需要确保以下处理程序返回true

      onStartShouldSetPanResponder: (evt, gestureState) => true,
      onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
      onMoveShouldSetPanResponder: (evt, gestureState) => true,
      onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,

onStart...onMove...之间的唯一区别是,当您开始为onStart...呈现组件时将创建PanResponder,它将在用户启动选项卡时创建(延迟)或移动onMove

在Android方面,您可能仍会发现onPanResponderRelease不会被触发,此处也会报告此问题https://github.com/facebook/react-native/issues/9447

我最终使用onPanResponderTerminate来处理此案例。希望你能获得更多有关它的见解。