2016-07-05 4 views
8

나는 안드로이드 개발을 위해 react-native을 사용하고 있습니다. 사용자가 길게 누르는 경우 드래그 할 수있는 애니메이션보기를 표시하려고하는보기가 있습니다. PanResponder을 사용하면 정상적으로 작동합니다.반응 - 네이티브 변경 응답자가 동적으로

하지만 내가하고 싶은 것은 사용자가 길게 누르는 경우 사용자가 새로 터치 한 Animated.View를 터치하거나 누르면서 계속 드래그 할 수 있어야한다는 것입니다.

Google 드라이브 앱에 익숙하다면 비슷한 기능을합니다. 사용자가 목록의 항목을 길게 누르면 드래그 가능한 항목이 표시됩니다. 사용자는 항목을 바로 드래그 할 수 있습니다. 그 다음이 작동 할 보여주는 시작 후 나는 드래그 항목에 동적으로 Responder으로 변경 될 가능성이있는 경우에

enter image description here

나는 생각한다.

질문은

는 네이티브 반응 동적 응답을 변경하는 방법을 제공합니까?

나는 그렇게 시도 무엇까지

  • 나는 onStartShouldSetPanResponderCapture의 논리, onMoveShouldSetPanResponderCapture, onMoveShouldSetPanResponder, onPanResponderTerminationRequest을 변경되도록 함께 노력 즉시 드래그 항목의 시작을 포착하지 말아야 컨테이너보기를 보여주는 시작으로 종료 요청을 이동하고 받아 들일 수 있습니다. 또한 드래그 가능한 항목의 종료 요청에 대해 false를 반환하고 이벤트를 캡처해야 함을 true로 반환합니다.

  • 저에게 효과가있는 해결 방법 중 하나는 불투명도가 낮은 컨테이너 상단에 드래그 가능한 항목을 표시하고 그 캡쳐를 false로 유지하는 것입니다. 사용자가 버튼을 길게 누르 자마자 명확하게 보이도록 불투명도를 변경하고 있습니다. 이 접근 방식을 통해 사용자는 계속해서 항목을 드래그 할 수 있습니다. 그러나 컨테이너는 실제로 목록 행입니다. 따라서 사용자가 임의의 행을 길게 누를 수 있으므로 많은 draggables를 만들어야합니다.

그러나 이것은 좋은 해결책이 아니며 응답자를 변경할 수 있다면 좋을 것 같습니다.

+0

관련 항목 : https://github.com/facebook/react-native/issues/7941 – mquandalle

답변

6

간단한 대답 본인이 아는

, 없는, 당신은 동적 뷰의 응답을 변경할 수 없습니다.

onStartShouldSetPanResponderCapture 같은 방법은 드래그하려는 아이 뷰에서 작동하지 않는 이유는, 그 방법이을 시작 터치에 해고되고 있다는 것입니다 정의의에 onStartShouldSetPanResponderCapture를 구현하는 것 아이 뷰에 의해 당신이 묘사하는 행동은 촉감이 시작될 때 아직 존재하지 않습니다.

하지만, 팬 응답자 방법은 아이 뷰에 구현해야 할 이유가 없다 : 필요한 실제 기능은

솔루션

다시 구현에서 발걸음을 내 디뎌은 그 일부 응용 프로그램의 구성 요소가 팬 응답자 여야합니다. 팬 반응기가 움직이면 터치 이벤트가 발생합니다. 이 시점에서 팬 동작의 변경 내용을 반영하기 위해 하위 뷰의 setNativeProps을 사용할 수 있습니다.

자식보기를 이동하려는 경우 실제로 그 자식을 응답자로 만들 필요가 없습니다. 간단하게 부모를 응답자로 만들고 부모로부터 자식 소품을 업데이트 할 수 있습니다.

여기 아래 예제 응용 프로그램을 구현하고, 한 무슨 일인지 단계의 설명에 의해 단계입니다 :

  1. 당신은 ListView 렌더링 구성 요소를 가지고있다. 그 ListView 귀하의 팬 응답자입니다. 목록보기의 각 셀에는 긴 프레스에 응답하는 TouchableOpacity이 있습니다.

  2. 긴 프레스 이벤트 (행에 의해 onLongPress 소품이 실행 됨)가 발생하면 부모 구성 요소를 맨 위에 떠있는보기로 다시 렌더링합니다. 이보기의 절대 위치는 상위 구성 요소 인 this._previousLeftthis._previousTop이 소유 한 두 가지 속성에 의해 제어됩니다.

  3. 이제이 부동 하위보기는 닿는 것에 신경 쓰지 않습니다. 부모는 이미 응답하고 있습니다. 모든 아이가 염려하는 것은 두 가지 위치 속성입니다. 떠 다니는 하위 구성 요소를 이동하려면 ListView에 의해 제공되는 _handlePanResponderMove 함수에서 하위 구성 요소의 setNativeProps 구성 요소를 사용하여 topleft 속성을 업데이트하면됩니다.

요약

당신이 접촉을 처리하고, 당신은하지 필요 구성 요소가 실제로 터치 이벤트를받는 하나가 이동중인 않습니다. 이동중인 구성 요소는 이 터치 이벤트를 수신하는 인 위치 속성을 업데이트하면됩니다.

다음은 Google 드라이브 앱에 기술 한 길게 누르/팬 제스처에 대한 전체 코드입니다 : 이것은 RN 0.29와 나를 위해 노력하고 있습니다

import React, { PropTypes } from 'react'; 
 
import { 
 
    AppRegistry, 
 
    ListView, 
 
    PanResponder, 
 
    StyleSheet, 
 
    Text, 
 
    TouchableOpacity, 
 
    View, 
 
} from 'react-native'; 
 

 
class LongPressDrag extends React.Component { 
 

 
    constructor() { 
 
    super(); 
 

 
    this._panResponder = PanResponder.create({ 
 
     onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder.bind(this), 
 
     onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder.bind(this), 
 
     onPanResponderMove: this._handlePanResponderMove.bind(this), 
 
     onPanResponderRelease: this._handlePanResponderEnd.bind(this), 
 
     onPanResponderTerminate: this._handlePanResponderEnd.bind(this), 
 
    }); 
 
    this._previousLeft = 0; 
 
    this._previousTop = 0; 
 
    this._floatingStyles = { 
 
     style: { 
 
     left: this._previousLeft, 
 
     top: this._previousTop, 
 
     position: 'absolute', 
 
     height: 40, 
 
     width: 100, 
 
     backgroundColor: 'white', 
 
     justifyContent: 'center', 
 
     } 
 
    }; 
 

 
    const rows = Array(11).fill(11).map((a, i) => i); 
 
    this.state = { 
 
     dataSource: new ListView.DataSource({ 
 
     rowHasChanged: (row1, row2) => row1 !== row2, 
 
     }).cloneWithRows(rows), 
 
     nativeEvent: undefined, 
 
     //Pan Responder can screw with scrolling. See https://github.com/facebook/react-native/issues/1046 
 
     scrollEnabled: true, 
 
    } 
 
    } 
 

 
    getDragElement() { 
 
    if (!this.state.nativeEvent) { 
 
     return null; 
 
    } 
 
    return (
 
     <View 
 
     style={[this._floatingStyles.style, 
 
      {top: this._previousTop, left: this._previousLeft} 
 
     ]} 
 
     ref={(floating) => { 
 
      this.floating = floating; 
 
     }} 
 
     > 
 
     <Text style={{alignSelf: 'center'}}>Floating Item</Text> 
 
     </View> 
 
    ) 
 
    } 
 

 
    render() { 
 
    return (
 
     <View> 
 
     <ListView 
 
      dataSource={this.state.dataSource} 
 
      renderRow={this.renderRow.bind(this)} 
 
      style={styles.container} 
 
      scrollEnabled={this.state.scrollEnabled} 
 
      {...this._panResponder.panHandlers} 
 
     /> 
 
     {this.getDragElement.bind(this)()} 
 
     </View> 
 
    ) 
 
    } 
 

 
    renderRow(num) { 
 
    return (
 
     <TouchableOpacity 
 
     style={styles.cell} 
 
     onLongPress={this.handleLongPress.bind(this)} 
 
     onPressIn={this.handlePressIn.bind(this)} 
 
     > 
 
     <Text style={styles.title}>{`Row ${num}`}</Text> 
 
     </TouchableOpacity> 
 
    ); 
 
    } 
 

 
    handleLongPress(event) { 
 
    console.log(event); 
 
    this.setState({ 
 
     nativeEvent: event.nativeEvent, 
 
     scrollEnabled: false, 
 
    }) 
 
    } 
 

 
    handlePressIn(event) { 
 
    this._previousLeft = event.nativeEvent.pageX - 50; 
 
    this._previousTop = event.nativeEvent.pageY - 20; 
 
    } 
 

 
    _updateNativeStyles() { 
 
    this.floating && this.floating.setNativeProps({style: {left: this._previousLeft, top: this._previousTop}}); 
 
    } 
 

 
    _handleStartShouldSetPanResponder(e, gestureState) { 
 
    return true; 
 
    } 
 

 
    _handleMoveShouldSetPanResponder(e, gestureState) { 
 
    return true; 
 
    } 
 

 
    _handlePanResponderMove(event, gestureState) { 
 
    this._previousLeft = event.nativeEvent.pageX - 50; 
 
    this._previousTop = event.nativeEvent.pageY - 20; 
 
    this._updateNativeStyles(); 
 
    } 
 

 
    _handlePanResponderEnd(e, gestureState) { 
 
    this._previousLeft += gestureState.dx; 
 
    this._previousTop += gestureState.dy; 
 
    this.setState({ nativeEvent: undefined, scrollEnabled: true}) 
 
    } 
 

 
} 
 

 
const styles = StyleSheet.create({ 
 
    container: { 
 
    flex: 1, 
 
    marginTop: 20, 
 
    }, 
 
    cell: { 
 
    flex: 1, 
 
    height: 60, 
 
    backgroundColor: '#d3d3d3', 
 
    borderWidth: 3, 
 
    borderColor: 'white', 
 
    justifyContent: 'center', 
 
    }, 
 
    title: { 
 
    paddingLeft: 20, 
 
    }, 
 
}); 
 

 
AppRegistry.registerComponent('LongPressDrag',() => LongPressDrag);

. 여기에 할 수있는 최적화가 많이 있지만, 해킹의 빠른 아침에 일반 개념을 설명하려고했습니다.

도움이 되었기를 바랍니다.

+0

Android에서 코드 스 니펫을 시도했습니다 (RN 0.29)'longPress'가 작동하지 않습니다. – mquandalle

+0

방금 ​​Android (Nexus 5X 에뮬레이터)에서 실행했는데 정상적으로 작동했습니다. 'onLongPress'가 해고 될 때까지 (iOS에 비해) 오랜 시간이 걸렸습니다. 그래서'delayLongPress' 소품 등으로 놀 필요가 있습니다. 하지만이 코드 스 니펫은 두 플랫폼에서 모두 잘 작동합니다. –

+0

@Michael Helvey를 테스트 해 주셔서 감사합니다. 불행히도 내 물리적 장치 나 에뮬레이터에서 여전히 작동하지 않지만이 특정 스 니펫과 관련이 없다고 생각됩니다. – mquandalle