- 原文部落格地址: ReactNative之手勢識別
- 移動開發中最重要的就是互動, 說到互動, 就不得不說觸控事件
- 在
iOS
中有單擊, 雙擊, 長按, 拖拽等觸控操作 - 在
React Native
中點選手勢都有其對應的元件, 每個元件都可以用來包裹檢視來響應使用者的點選事件
TouchableWithoutFeedback
- 響應使用者的點選事件, 點選操作時, 元件沒有任何視覺反饋,看起來像
Web
效果而不是原生的效果Native
- 是單節點元件, 只能包含一個元件, 如果你希望包含多個子元件,用一個View來包裝它們
- 該控制元件除非你不得不使用,否則請不要使用該元件
屬性方法
-
accessibilityComponentType
:View.AccessibilityComponentType
設定可訪問的元件型別 -
accessibilityTraits
:View.AccessibilityTraits,[View.AccessibilityTraits]
設定訪問特徵 -
accessible
:bool
設定當前元件是否可以訪問 -
delayLongPress
:number
設定延遲的時間,單位為毫秒。從onPressIn
方法開始,到onLongPress
被呼叫這一段時間 -
delayPressIn
:number
設定延遲的時間,單位為毫秒,從使用者觸控控制元件開始到onPressIn
被呼叫這一段時間 -
delayPressOut
:number
設定延遲的時間,單位為毫秒,從使用者觸控事件釋放開始到onPressOut
被呼叫這一段時間 -
onLayout
:function
當元件載入或者改元件的佈局發生變化的時候呼叫, 呼叫傳入的引數為{nativeEvent:{layout:{x,y,width,height}}}
-
onLongPress
:function
當使用者長時間按壓元件(長按效果)的時候呼叫該方法 -
onPress
:function
當使用者點選的時候呼叫(觸控結束)。 但是如果事件被取消了就不會呼叫。(例如:當前被滑動事件所替代) -
onPressIn
:function
使用者開始觸控元件回撥方法 -
onPressOut
:function
使用者完成觸控元件之後回撥方法 -
pressRetentionOffset
:{top:number,left:number,bottom:number,right:number}
該設定當檢視滾動禁用的情況下,可以定義當手指距離元件的距離; 當大於該距離該元件會失去響應;當少於該距離的時候,該元件會重新進行響應
該元件我們一般不會直接進行使用,下面三種
Touchable*
系列元件對於該元件的屬性方法都可以進行使用
TouchableOpacity
該元件封裝了響應觸控事件。當點選按下的時候,該元件的透明度會降低。該元件使用過程中並不會改變檢視的層級關係,而且我們可以非常容易的新增到應用並且不會產生額外的異常錯誤
屬性方法
TouchableWithoutFeedback
的所有 屬性,這邊TouchableOpacity
元件全部可以進行使用activeOpacity
:number
---設定當使用者觸控的時候,元件的透明度(取值0-1)
TouchableHighlight
當手指點選按下的時候,該檢視的不透明度會進行降低同時會看到相應的顏色(檢視變暗或者變亮)。如果我們去檢視該元件的原始碼會發現,該底層實現是新增了一個新的檢視
屬性方法
- 所有
TouchableWithoutFeedback
的屬性 activeOpacity
:number
---該用來設定檢視在進行觸控的時候,要要顯示的不透明度(通常在0-1之間)onHideUnderlay
:function
---當底層被隱藏的時候呼叫onShowUnderlay
:function
---當底層顯示的時候呼叫underlayColor
: 當觸控或者點選控制元件的時候顯示出的顏色
TouchableNativeFeedback
- 僅限
Android
平臺 - 在
Android
裝置上,這個元件利用原生狀態來渲染觸控的反饋 - 目前它只支援一個單獨的
View
例項作為子節點
屬性方法
- 所有
TouchableWithoutFeedback
的屬性 background
: 決定在觸控反饋的時候顯示什麼型別的背景
PanResponder
相關介紹
PanResponder
類可以將多點觸控操作協調成一個手勢。它使得一個單點觸控可以接受更多的觸控操作,也可以用於識別簡單的多點觸控手勢
手勢處理
React Native
框架底層的手勢響應系統提供了響應處理器,PanResponder
將這些手勢響應處理器再次進行封裝,以便開發者更容易對手勢進行處理,更容易預測使用者的手勢,對每一個手勢響應處理器,PanResponder
除了為其提供代表觸控行為的原生事件外,還提供了一個新的手勢狀態物件用來詳細描述手勢的狀態
基本思想是:
監視螢幕上指定大小、位置的矩形區域,當用手指按壓這個區域中的某點後,開發者會收到這個事件,當按壓後拖動手指時,會收到手勢引發的各類事件,當手指離開這個矩形區域時,開發者也會收到相應的事件
注意事項:
- 開發者可以任意指定監視矩形區域的大小,但在這個區域裡,只有第一個按下的事件會上報和繼續監視處理,如果第一個手指按下還沒有離開,接著第二個手指又來按下了,那麼對第二個手指的各種觸控事件無法捕獲
- 開發者可以在螢幕上指定多個監視矩形區域,但是不能同時監視多個矩形區域的不同觸控事件
- 監視區域會阻止被監視區域覆蓋的元件接收觸控事件,比如監視區域覆蓋了一個按鈕,那麼就無法通過按這個按鈕來觸發其對應的事件,只能在
PanResponder
監視器的事件處理中對觸控行為進行處理
使用操作
利用PanResponder
實現監視器有以下幾個步驟:
指定監視區域
如果監視區域有多個,一定不能重疊,否則都失效
定義監視器相關變數
指向監視器的變數(必須有)、指向監視器監視區域的變數(可以有)、記錄監視區域左上角頂點座標的兩個數值變數(可以有)、上一次觸控點的橫縱座標變數(可以有)
事件處理
準備監視器的事件處理函式
建立監視器
PanResponder.create(config)
相關事件監聽
// 返回值為布林值, 如果返回值為 true,則表示這個 View 能夠響應滑動手勢, 兩者有一個為true即可響應
onMoveShouldSetPanResponder: (e, gestureState) => {...}
onMoveShouldSetPanResponderCapture: (e, gestureState) => {...}
// 返回值為布林值, 如果返回值為 true,則表示這個 View 能夠響應觸控手勢, 兩者有一個為true即可響應
onStartShouldSetPanResponder: (e, gestureState) => {...}
onStartShouldSetPanResponderCapture: (e, gestureState) => {...}
// 當前有其他的東西成為響應器並且沒有釋放它。如果檢視正在響應,會觸發該方法
onPanResponderReject: (e, gestureState) => {...}
// 最近一次的移動距離.如:(獲取x軸方向的移動距離 gestureState.dx)
onPanResponderGrant: (e, gestureState) => {...}
// 開始按下時的響應事件
onPanResponderStart: (e, gestureState) => {...}
// 結束按下時的響應事件
onPanResponderEnd: (e, gestureState) => {...}
// 使用者手指離開螢幕時,呼叫該方法
onPanResponderRelease: (e, gestureState) => {...}
// 使用者滑動手指時,呼叫該方法
onPanResponderMove: (e, gestureState) => {...}
// 另一個元件已經成為了新的響應者,所以當前手勢將被取消
onPanResponderTerminate: (e, gestureState) => {...}
// 如果回撥函式返回為 true,則表示同意釋放響應者角色 同時會回撥onResponderTerminate函式,通知元件事件響應處理被終止了
onPanResponderTerminationRequest: (e, gestureState) => {...}
// 返回一個布林值,決定當前元件是否應該阻止原生元件成為JS響應者(暫只支援android)
onShouldBlockNativeResponder: (e, gestureState) => {...}
複製程式碼
監視器與監視區域關聯
{…this.watcher.panHandlers}
例項:點選、拖動選擇百分百引數 比如說播放器的音量大小
引數event
(e
)
獲取觸控的位置在被響應的 View 中的相對座標,evt.nativeEvent.locationX
nativeEvent
changedTouches
- 在上一次事件之後,所有發生變化的觸控事件的陣列集合(即上一次事件後,所有移動過的觸控點)identifier
- 觸控點的IDlocationX
- 觸控點相對於父元素的橫座標locationY
- 觸控點相對於父元素的縱座標pageX
- 觸控點相對於根元素的橫座標pageY
- 觸控點相對於根元素的縱座標target
- 觸控點所在的元素IDtimestamp
- 觸控事件的時間戳,可用於移動速度的計算touches
- 當前螢幕上的所有觸控點的集合
gestureState
物件
stateID
-- 觸控狀態的ID。在螢幕上有至少一個觸控點的情況下,這個ID會一直有效。moveX
- 最近一次移動時的螢幕橫座標moveY
- 最近一次移動時的螢幕縱座標x0
- 當響應器產生時的螢幕座標y0
- 當響應器產生時的螢幕座標dx
- 從觸控操作開始時的累計橫向路程dy
- 從觸控操作開始時的累計縱向路程vx
- 當前的橫向移動速度vy
- 當前的縱向移動速度numberActiveTouches
- 當前在螢幕上的有效觸控點的數量
使用示例
相關程式碼
export default class MyApp extends Component {
constructor(props) {
super(props)
this.state = {
backColor: 'red',
left: 0,
top: 100
}
}
componentWillMount() {
this._panResponse = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onStartShouldSetPanResponderCapture: () => false,
onMoveShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
this._top = this.state.top
this._left = this.state.left
this.setState({ backColor: 'red' })
},
onPanResponderMove: (event, ges) => {
console.log(`event = ${event}, guesture = ${ges}`)
this.setState({
top: this._top + ges.dy,
left: this._left + ges.dx,
backColor: 'blue'
})
},
onPanResponderRelease: (event, ges) => {
this.setState({
backColor: 'orange'
})
}
})
}
render() {
return (
<View style={styles.container}>
<View
{...this._panResponse.panHandlers}
style={{
position: 'absolute',
backgroundColor: this.state.backColor,
left: this.state.left,
top: this.state.top,
width: 50,
height: 50
}}
/>
</View>
);
}
}
複製程式碼