標籤:
一、Touchable手勢1.React Native提供了4個組件來做這個事情,具體如下: TouchableHighlight:高亮觸摸,使用者點擊時,會產生高亮效果; TouchableNativeFeedback: TouchableOpacity:透明觸摸,使用者點擊時,點擊的組件不會出現任何視覺變化; TouchableWithoutFeedback:無反饋觸摸,使用者點擊時,點擊的組件不會有任何視覺變化;2.這4個組件,我們可以應用某個部分綁定上Touch事件,並支援一下方法: onPress: onPressIn: onPressOut: onLongPress:3.下面我們以執行個體示範下,相關代碼實現:Index.android.js檔案:
import React, { … … TouchableHighlight,} from ‘react-native‘;class AwesomeProject extends Component { show(text) { alert(text); } //手勢相關事件的實現 onPressIn(){ this.start = Date.now() console.log("press in") } onPressOut(){ console.log("press out") } onPress(text){ console.log("press") alert(text); } onLonePress(){ AppRegistry, console.log("long press "+(Date.now()-this.start)) } render() { return( <View style={styles.container}> //TouchableHighlight包裹綁定Touch手勢的組件,並實現支援的4個事件 <TouchableHighlight style={styles.touchable} onPressIn={this.onPressIn} onPressOut={this.onPressOut} onPress={this.onPress.bind(this,‘點擊了嗎?‘)} onLongPress={this.onLonePress}> <View style={styles.button}></View> </TouchableHighlight> </View> ) };}var styles = StyleSheet.create({ container: { flex: 1, justifyContent: ‘center‘, alignItems: ‘center‘, backgroundColor: ‘#F5FCFF‘, }, button:{ width: 200, height: 200, borderRadius: 100, backgroundColor: ‘red‘ }, touchable: { borderRadius: 100 },});AppRegistry.registerComponent(‘AwesomeProject‘, () => AwesomeProject);
4.點擊,我們看見具體的運行效果如下:
5.我們使用Debug模式,研究下4個手勢出現的條件和順序;搖晃手機,選擇Debug JS;
開啟Chrome瀏覽器,在彈出的Debug頁面http://localhost:8081/debugger-ui後,選擇開發著工具,點擊手機上的按鈕就可以看見瀏覽器控制台的輸出內容PressIn->longPress->pressOut;
二、手勢響應生命週期1.對於大部分應用,使用以上4個Touch*組件,在配合4個Press事件就能對使用者的手勢進行響應。但是對於比較複雜的互動,還得使用手勢響應系統;2.響應手勢的基本單位是responder,具體來說就是View組件,任何View組件都是潛在的responder;3.一個普通的View組件成為能響應手勢操作的responder,只要設定幾個手響應生命週期的方法即可,具體如下: View.props.onStartShouldSetResponder:使用者開始觸控螢幕幕的時候,是否願意成為響應者; View.props.onMoveShouldSetResponder:在每一個觸摸點開始移動的時候,再詢問一次是否響應觸摸互動; View.props.onResponderGrant:要開始響應觸摸事件了; View.props.onResponderReject:響應者現在另有其人,而且暫時不會放權,另作安排; View.props.onResponderMove:使用者正在螢幕上移動手指; View.props.onResponderRelease:觸摸操作結束收觸發; View.props.onResponderTerminationRequest:有其它組件請求接替響應者,當前View是否放權; View.props.onResponderTerminate:響應權已經交出;4.一個React Native應用中同時之能存在一個responder,具體響應步驟如下: 使用者通過觸摸或者滑動來“啟用”某個responder,View.props.onStartShouldSetResponder以及View.props.onMoveShouldSetResponder這兩個方法處理,如果返回true,則這個View能夠響應觸摸或者滑動手勢被啟用; 如果組件被啟用,View.props.onResponderGrant方法被調用,一般這個時候去改變組建的底色或者透明度,表示組件已經被啟用; 接下來,使用者開始滑動手指,此時View.props.onResponderMove方法被調用; 當使用者的手指離開螢幕之後,View.props.onResponderRelease方法被調用,組件恢複被觸摸之前樣式,例如底色和透明度恢複之前的樣式,完成一次手勢操作;正常流程:響應touch或者move手勢 -> grant(被啟用) -> move -> release(結束事件);
5.下面我們以執行個體示範下,相關代碼如下:index.android.js檔案
import React, { … … } from ‘react-native‘;var AwesomeProject = React.createClass({ getInitialState(){ return { bg: ‘white‘ }; }, componentWillMount(){ this._gestureHandlers = { onStartShouldSetResponder: () => true, onMoveShouldSetResponder: ()=> true, onResponderGrant: ()=>{ this.setState({bg: ‘red‘}) }, onResponderMove: ()=>{ console.log(123) }, onResponderRelease: ()=>{ this.setState({bg: ‘white‘}) } } }, render() { return( <View style={styles.container}> <View {...this._gestureHandlers} style={[styles.rect, {backgroundColor:this.state.bg}]}></View> </View> ); }});var styles = StyleSheet.create({ … … });AppRegistry.registerComponent(‘AwesomeProject‘, () => AwesomeProject);
6.運行效果如下,onClick為未點擊,Click為點擊,後面為控制台輸出log:
注意:如果運行Demode的時候錯誤提示如:
使用var AwesomeProject = React.createClass();的方式建立組件;
三、手勢事件傳遞1.onStartShouldSetResponder於onMoveShouldSetResponder是以冒泡的形式調用的,即嵌套最深的節點最先調用;2.意味著多個View同時在ShouldSetResponder中返回true時,最底層的View將優先”奪權“;3.但是有些時候,某個父View會希望先能成為響應者,我們可以利用”捕獲期“來解決。響應系統從最底層的組件開始冒泡前,會首先執行一個”捕獲期“,在此期間會觸發on*ShouldSetResponderCapture系列事件。因此,如果某個父View想要在觸摸開始時阻止組件成為響應者,那就應該處理onStartShouldSetResponderCapture事件冰返回true值; View.props.onStartShouldSetResponderCapture:(evt)=>true; View.props.onMoveShouldSetReponderCapture(evt)=>ture;
4.下面我們將以執行個體示範一下,實現代碼如下:Index.android.js檔案:
import React, { ... ... } from ‘react-native‘;var AwesomeProject = React.createClass({ getInitialState(){ return { bg: ‘white‘, bg2: ‘white‘ } }, componentWillMount(){ this._gestureHandlers = { //外部正方形在“捕獲期”阻止底層時間成為響應者 onStartShouldSetResponderCapture: () => true, onMoveShouldSetResponderCapture: ()=> true, onResponderGrant: ()=>{this.setState({bg: ‘red‘})}, onResponderMove: ()=>{console.log(123)}, onResponderRelease: ()=>{this.setState({bg: ‘white‘})}, } this._gestureHandlers2 = { //內部正方形在即時實現了on*ShouldSetResponder也無法成為響應者 onStartShouldSetResponder: () => true, onMoveShouldSetResponder: ()=> true, onResponderGrant: ()=>{this.setState({bg2: ‘green‘})}, onResponderMove: ()=>{console.log(123)}, onResponderRelease: ()=>{this.setState({bg2: ‘white‘})} } }, render: function() { return ( <View style={styles.container}> <View {...this._gestureHandlers} style={[styles.rect,{"backgroundColor": this.state.bg}]}> <View {...this._gestureHandlers2} style={[styles.rect2,{"backgroundColor": this.state.bg2}]}> </View> </View> </View> ); }});var styles = StyleSheet.create({ ... ... });AppRegistry.registerComponent(‘AwesomeProject‘, () => AwesomeProject);
5.運行效果如,點擊內部正方形,外部正方形相應事件:
四、evt參數1.和Web開發中的事件參數類似,以上的每個方法都有一個evt參數,在事件發生的過程中,這個evt參數的nativeEvent屬性的各個指能表示手勢的狀態: nativeEventchangedTouches:在上一次事件之後,所有發生變化的觸摸事件的數組集合(即上一次事件後,所有移動過的觸摸點) identifier:觸摸點的ID locationX:觸摸點相對於父元素的橫座標 locationY:觸摸點相對於父元素的縱座標 pageX:觸摸點相對於根項目的橫座標 pageY:觸摸點相對於根項目的縱座標 target:觸摸點所在的元素ID timestamp:觸摸事件的時間戳記,可用於移動速度的計算 touches:當前螢幕上的所有觸摸點的集合
五、PanResponder1.除了手勢相應系統之外,React Native還抽象出一套PanResponder方法,它的抽象成程度更高,使用起來更為方便;2.使用PanResponder的時候,相應手勢的邏輯和流程都不變,只需要根據文檔對幾個方法名稱參數修改即可: 第一個參數evt; 第二個gestureState,包含手勢進行過程中更多資訊,比較常見如下: dx/dy:手勢進行到現在的橫向/縱向相對位移; vx/vy:此刻的橫向/縱向速度; numberActiveTouches:reponder上的觸摸的個數;3.下面我們就使用PanResponder實現拖拽效果,代碼實現如下:index.android.js檔案:
import React, { ... ... PanResponder,} from ‘react-native‘;var AwesomeProject = React.createClass({ getInitialState(){ return { bg: ‘white‘, top: 0, left: 0 } }, componentWillMount(){ this._panResponder = PanResponder.create({ onStartShouldSetPanResponder: () => true, onMoveShouldSetPanResponder: ()=> true, onPanResponderGrant: ()=>{ //滑動開始時,擷取矩形的左上座標,並設定背景為紅色 this._top = this.state.top this._left = this.state.left this.setState({bg: ‘red‘}) }, onPanResponderMove: (evt,gs)=>{ console.log(gs.dx+‘ ‘+gs.dy) //隨著手勢滑動,相應的改變矩形的位置 this.setState({ top: this._top+gs.dy, left: this._left+gs.dx }) }, onPanResponderRelease: (evt,gs)=>{ //活動結束後,還原背景為白色 this.setState({ bg: ‘white‘, top: this._top+gs.dy, left: this._left+gs.dx })} }) }, render: function() { return ( <View style={styles.container}> //設定手勢事件處理對象 <View{...this._panResponder.panHandlers} style={[styles.rect,{ "backgroundColor": this.state.bg, "top": this.state.top, "left": this.state.left}]}> </View> </View> ); }});var styles = StyleSheet.create({ container: { flex: 1, //開始的矩形位於中間,拖動到下部地區 justifyContent: ‘center‘, alignItems: ‘center‘, backgroundColor: ‘#F5FCFF‘, }, rect: { ... ... }});AppRegistry.registerComponent(‘AwesomeProject‘, () => AwesomeProject);
4.運行示範如下,由於只能上傳圖片,無法示範動畫,樣式中矩形位於中間,圖中拖動至下方:
React Native 五:手勢