首页 > 解决方案 > 拖动手指时取消按钮单击

问题描述

目标

onResponderRelease()当用户的手指离开它时取消自定义按钮。有没有我在网上找不到的标准方法?

我试过的

onLayout用于抓取组件的位置和onResponderMoveGestureResponderEventpageX/pageY 来计算点击区域。但是,onLayout 仅获取相对于父级的位置,因此这不适用于模态。

  const handleMove = (e: GestureResponderEvent) => {
    const { pageX, pageY } = e.nativeEvent
    if (pageX < minX || pageX > maxX || pageY < minY || pageY > maxY) {
      setCancelClick(true)
      setLongPressed(false)
      setPressed(false)
    }
  }

  const setComponentDimensions = ({ nativeEvent }: LayoutChangeEvent) => {
    const { x, y, width, height } = nativeEvent.layout
    setMinX(x - BUFFER)
    setMaxX(x + width + BUFFER)
    setMinY(y - BUFFER)
    setMaxY(y + height + BUFFER)
  }

  return (
    <View
      style={[style, pressed && pressedStyle]}
      onStartShouldSetResponder={() => true}
      onResponderGrant={() => setPressed(true)}
      onResponderRelease={handleRelease}
      onResponderMove={handleMove}
      onLayout={setComponentDimensions}
      onResponderTerminationRequest={() => false}
      {...ViewProps}>
      {children}
    </View>
  )

其他想法

我也考虑过使用 auseRef和 ref's measure。这可能会奏效。我正在使用的另一个库中的组件似乎没有measure它的引用,所以我担心这可能不是最灵活的解决方案。

  const viewRef = useRef(null as View | null)

  useEffect(() => {
    if (viewRef.current) {
      // TODO: base calculations off of measure
      console.log(viewRef.current.measure)
    }
  }, [viewRef.current])

  ...

  return (
    <View
      ref={(view) => (viewRef.current = view)}
      ...
      {children}
    </View>
  )

标签: react-nativegesture

解决方案


measure()下列方式使用似乎对我有用。

  const viewRef = useRef(null as View | null)
  const [dimensions, setDimensions] = useState({
    minX: 0,
    maxX: 0,
    minY: 0,
    maxY: 0,
  })

  const handleMove = (e: GestureResponderEvent) => {
    const { pageX, pageY } = e.nativeEvent
    const { minX, maxX, minY, maxY } = dimensions

    if (pageX < minX || pageX > maxX || pageY < minY || pageY > maxY) {
      setLongPressed(false)
      setPressed(false)
    }
  }

  const setComponentDimensions = ({ nativeEvent }: LayoutChangeEvent) => {
    const { width, height } = nativeEvent.layout
    if (viewRef.current) {
      viewRef.current.measure((_a, _b, _width, _height, px, py) => {
        setDimensions({
          minX: px - BUFFER,
          maxX: px + width + BUFFER,
          minY: py - BUFFER,
          maxY: py + height + BUFFER,
        })
      })
    }
  }

  return (
    <View
      ref={(view) => (viewRef.current = view)}
      onStartShouldSetResponder={() => true}
      onResponderMove={handleMove}
      onLayout={setComponentDimensions}
      onResponderTerminationRequest={() => false}
      {children}
    </View>
  )

更新

在稍微重构了我的组件并且之前从未尝试过之后,我决定发布一个包。希望它可以帮助其他人:react-native-better-buttons

我最终得到了一个更简单的钩子,只需要放在组件的 ref 上

const useMeasurement = (buffer = 0) => {
  const ref = useRef<View | null>(null)
  const [dimensions, setDimensions] = useState({
    minX: 0,
    maxX: 0,
    minY: 0,
    maxY: 0,
  })

  /* This goes on the view's ref property */
  const setRef = (view: View) => (ref.current = view)

  /* 
     Updating ref.current does not cause rerenders, which is fine for us.
     We just want to check for ref.current updates when component that uses
     us rerenders.
  */
  useEffect(() => {
    if (ref.current && ref.current.measure) {
      ref.current.measure((_a, _b, width, height, px, py) => {
        setDimensions({
          minX: px - buffer,
          maxX: px + width + buffer,
          minY: py - buffer,
          maxY: py + height + buffer,
        })
      })
    }
  }, [ref.current])

  return { dimensions, setRef }
}

推荐阅读