react-native - 检测手指移入视图

问题描述 投票:0回答:1

我正在研究如何在 React Native 中检测手指移动到视图中。例如,我在屏幕上有一个视图。我的手指按在视图之外,慢慢移向视图。当手指越过边界时,视图可以识别我的手指。

我研究了Gesture ResponderPanResponder。但我发现的所有例子都触及了观点并感动了它。

如果您有方法或示例代码来执行我上面所说的操作,请告诉我。

我需要做一些数学计算才能到达目的地吗?

提前谢谢您!

android ios react-native uigesturerecognizer uipangesturerecognizer
1个回答
0
投票

@LuongTrong 之前的评论谈到了这一点,但一旦您掌握了 PanResponder 的作用及其工作原理,它并不像听起来那么难。 您需要检测用户何时将手指拖动到视图上的方法是使用 PanResponder、view.onLayout 和视图引用。 当您想要跟踪的视图接触到加载和脱离加载时,捕获视图的 4 个角(onLayout 为您提供 x、y 和高度、宽度,因此需要进行一些添加)。使用 onPanResponderMove 方法,检查触摸点是否在视图的边界内,然后相应地更新 ref 的状态。

听起来比实际更复杂,这里有一个 App.js(100% 演示代码)演示了它是如何工作的。我已将“按钮”组件分解为一个单独的组件,以使其更可重用。

import React, {useEffect, useState} from 'react'
import {setStatusBarBackgroundColor, StatusBar} from 'expo-status-bar'
import {PanResponder, Pressable, StyleSheet, Text, TouchableOpacity, View} from 'react-native'
import {GestureHandlerRootView, PanGestureHandler} from 'react-native-gesture-handler'
import {useEvent} from 'react-native-reanimated'

const CustButton = React.forwardRef((props, ref) => {
  const [viewLayout, setViewLayout] = useState({})
  const selColor = 'red'
  const unselectedColor = 'blue'
  const [selectedColor, setSelectedColor] = useState(unselectedColor)

  function getViewLatout() {
    return viewLayout
  }

  const updateSelectionStatus = (isActive) => {
    setSelectedColor(isActive ? selColor : unselectedColor)
  }

  const checkPoint = (x,y) => {
    const {top, left, right, bottom} = viewLayout
    return (x > left && x < right && y > top && y < bottom)
  }

  React.useImperativeHandle(ref, () => ({
    // each key is connected to `ref` as a method name
    // they can execute code directly, or call a local method
    setActive: (isActive) => { updateSelectionStatus(isActive) },
    isPointInView: (x,y) => { return checkPoint(x,y)}
  }))

  return (<View
    ref={ref}
    style={[styles.button, {backgroundColor: selectedColor}]}
    onLayout={(evt) => {

      const {x, y, height, width} = evt.nativeEvent.layout
      const viewLayoutProp = {
        top: y,
        bottom: y + height,
        right: x + width,
        left: x
      }
      setViewLayout(viewLayoutProp)
  }}>
    <Text style={{color: 'white', fontSize: 24, fontWeight: 'bold'}}>move over me</Text>
  </View>)
})

export default function App() {

  const viewRef = React.useRef()
  const viewRef2 = React.useRef()
  const viewRef3 = React.useRef()
  const checkIfAViewIsSelected = (evt) => {
    const {pageX:x, pageY:y} = evt.nativeEvent
    viewRef.current.setActive(viewRef.current.isPointInView(x,y))
    viewRef2.current.setActive(viewRef2.current.isPointInView(x,y))
    viewRef3.current.setActive(viewRef3.current.isPointInView(x,y))
  }

  const panResponder = React.useMemo(() =>
      PanResponder.create({
          onStartShouldSetPanResponder: (evt, gestureState) => true,
          onMoveShouldSetPanResponder: (evt, gestureState) => true,
          onPanResponderTerminationRequest: (evt, gestureState) => true,
          onPanResponderRelease: (evt, gestureState) => {
          },
          onPanResponderStart:(evt) => {
            checkIfAViewIsSelected(evt)
          },
          onPanResponderMove: (evt, gestureState) => {
              checkIfAViewIsSelected(evt)
          }
      }), [viewRef, viewRef2, viewRef3])

  useEffect(() => {
    if (viewRef.current == null) {
      return;
    }
  }, [viewRef.current])

  return (
    <View style={styles.container}>

          <View style={[styles.container, {backgroundColor: '#fff'}]}
                {...panResponder.panHandlers}
          >
            <CustButton ref={viewRef}  />
            <CustButton ref={viewRef2} />
            <CustButton ref={viewRef3} />

            <StatusBar style="auto"/>
          </View>
    </View>
  )
}

const styles = StyleSheet.create(
  {
   container: {
     flex: 0,
     backgroundColor: '#555',
     alignItems: 'center',
     justifyContent: 'center',
     gap: 20,
     height: '100%',
       width:'100%'
   },
   button: {
     flex: 0,
     height: 100,
     width: 250,
     backgroundColor: 'red',
     alignItems: 'center',
     justifyContent: 'center'
   }
 })

真正的问题是:

  • 返回子视图并调用子视图的方法。您需要对子级的引用,这是通过forwardRef完成的,
  • 在子进程中暴露方法必须使用 useImperativeHandle 来完成,否则 ref 无法看到这些方法,并且如果使用它们将会崩溃。
  • 确保您的父视图包含 {...panResponders.panHandlers},
  • 我必须使用 useMemo 而不是 useRef 来制作 panHandler,如 React Native 网站上的示例所示。使用 useRef 时,我没有得到任何更新的视图引用。

希望这对您或偶然发现这篇文章的其他人有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.