当手指被拖离时取消按钮点击
Cancel button click when finger is dragged off
目标
当用户的手指离开自定义按钮时取消 onResponderRelease()
。有没有我在网上找不到的标准方法?
我试过的
利用onLayout
抓取组件的位置,利用onResponderMove
的GestureResponderEvent
的pageX/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>
)
其他想法
我也考虑过使用 useRef
和裁判的 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>
)
按以下方式使用 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.
我最终得到了一个更简单的钩子,它只需要放在组件的引用上
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 }
}
目标
当用户的手指离开自定义按钮时取消 onResponderRelease()
。有没有我在网上找不到的标准方法?
我试过的
利用onLayout
抓取组件的位置,利用onResponderMove
的GestureResponderEvent
的pageX/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>
)
其他想法
我也考虑过使用 useRef
和裁判的 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>
)
按以下方式使用 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.
我最终得到了一个更简单的钩子,它只需要放在组件的引用上
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 }
}