ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

React Native Gesture Handler v2 核心组件 GestureDetector 完全指南:组合手势、Web 属性与底层实现

React Native Gesture Handler v2 核心组件 GestureDetector 完全指南:组合手势、Web 属性与底层实现 移动开发UI组件【免费下载链接】react-native-gesture-handlerDeclarative API exposing platform native touch and gesture system to React Native.项目地址https://gitcode.com/gh_mirrors/re/react-native-gesture-handler点击查看免费下载GestureDetector是 react-native-gesture-handlerRNGH2.x 新版 API 的核心组件它负责根据传入手势对象的配置创建并更新原生手势处理器并通过手势组合Gesture Composition让同一个视图同时识别多种手势。本文基于本仓库 v2 文档与packages/react-native-gesture-handler/src下的源码实现完整讲解GestureDetector的用法、属性、Web 专属配置、collapsable 陷阱与底层挂载/更新流程帮助你在真实项目中正确、稳健地使用新 API。GestureDetector 是什么GestureDetector是 RNGH2 的主组件。与旧版直接使用PanGestureHandler、TapGestureHandler等独立组件的 API 相比它的职责更加集中根据你提供的手势对象的配置负责创建和更新对应的原生手势处理器。import { Gesture, GestureDetector } from react-native-gesture-handler; function App() { const tap Gesture.Tap(); return ( GestureDetector gesture{tap} Animated.View / /GestureDetector ); }新旧 API 之间最显著的区别在于借助手势组合gesture compositionGestureDetector可以同时识别一种以上的手势。你可以把多个基础手势Tap、Pan、LongPress、Fling、Pinch、Rotation、ForceTouch通过Race、Simultaneous、Exclusive三种组合器组装成一个复合手势对象再一次性交给GestureDetector。需要注意的是GestureDetector不兼容React Native 官方的 Animated API也不兼容 Reanimated 1.x。如果需要让手势与动画同步联动应当使用 Reanimated 2 及以上版本本仓库的 v3 API 更是深度依赖 Reanimated 2/3 的 worklet 能力。基本用法GestureDetector的用法非常直观通过gesture属性传入一个手势对象并将其作为包裹目标视图的父组件import { Gesture, GestureDetector } from react-native-gesture-handler; function App() { const tap Gesture.Tap(); return ( GestureDetector gesture{tap} Animated.View / /GestureDetector ); }GestureDetector接受且仅接受一个子组件。如果传入多个子视图组件会直接抛错。在 Wrap.tsx 的源码中可以看到这一校验逻辑try { child React.Children.only(this.props.children); } catch (e) { throw new Error( tagMessage( GestureDetector got more than one view as a child. If you want the gesture to work on multiple views, wrap them with a common parent and attach the gesture to that view. ) ); }因此如果你希望手势作用于多个视图正确的做法是先用一个共同的父视图把它们包起来再把手势附加到这个父视图上。属性PropertiesGestureDetector的全部属性定义可以在 index.tsx 的GestureDetectorProps接口中找到包括一个核心属性gesture和三个 Web 专属属性。gesture一个包含配置与回调的手势对象。可以是任意基础手势Tap、Pan、LongPress、Fling、Pinch、Rotation、ForceTouch也可以是任意ComposedGestureRace、Simultaneous、Exclusive。以组合手势为例import { GestureDetector, Gesture } from react-native-gesture-handler; function App() { const pan Gesture.Pan(); const longPress Gesture.LongPress(); const composed Gesture.Race(pan, longPress); return ( GestureDetector gesture{composed} Animated.View / /GestureDetector ); }在内部实现中GestureDetector会调用gestureConfig.toGestureArray()把可能组合过的手势对象展开为待挂载的基础手势数组并据此决定后续的挂载策略见 index.tsxconst gesturesToAttach useMemo( () gestureConfig.toGestureArray(), [gestureConfig] ); const shouldUseReanimated gesturesToAttach.some( (g) g.shouldUseReanimated );infoReanimated 的接入方式GestureDetector会根据手势携带的回调来决定是否使用 Reanimated 来处理这些手势。只要任意一个回调是 worklet就会启用 Reanimated 提供的工具从而获得同步处理手势的能力反之则走纯 JS 回调路径。另外从 Reanimated 2.3.0 开始Gesture Handler 会在 touch events 中提供 StateManager允许你在手势处理过程中管理手势状态。shouldUseReanimated这一判断直接影响渲染路径当需要 Reanimated 时GestureDetector渲染AnimatedWrap即用 Reanimated 的createAnimatedComponent包装后的Wrap否则渲染普通的Wrap见 index.tsxif (shouldUseReanimated) { return ( AnimatedWrap ref{refHandler} onGestureHandlerEvent{preparedGesture.animatedEventHandler} {props.children} /AnimatedWrap ); } else { return Wrap ref{refHandler}{props.children}/Wrap; }在挂载手势时这个决定会进一步转化为原生层的动作类型ActionType见 attachHandlers.tsfor (const gesture of gesturesToAttach) { const actionType gesture.shouldUseReanimated ? ActionType.REANIMATED_WORKLET : ActionType.JS_FUNCTION_NEW_API; // ... RNGestureHandlerModule.attachGestureHandler( gesture.handlerTag, viewTag, actionType ); // ... }userSelect仅 Web指定应用于底层视图的userSelectCSS 属性。可选值为none | auto | text默认值为none。touchAction仅 Web指定应用于底层视图的touchActionCSS 属性。支持所有 CSStouch-action取值例如none、pan-y默认值为none。enableContextMenu(value: boolean)仅 Web指定在底层视图上点击鼠标右键后是否启用上下文菜单。默认值为false。Web 属性的传播机制这三个 Web 属性并非只影响GestureDetector外壳本身而是会被逐项传播到每一个待挂载的手势配置中。在 index.tsx 的propagateDetectorConfig中可以看到const keysToPropagate: (keyof GestureDetectorProps)[] [ userSelect, enableContextMenu, touchAction, ]; for (const key of keysToPropagate) { const value props[key]; if (value undefined) { continue; } for (const g of gesture.toGestureArray()) { const config g.config as { [key: string]: unknown }; config[key] value; } }也就是说即使你传入的是组合手势这三个配置也会被复制到展开后的每个基础手势的config上最终通过filterConfig过滤后交给原生/Web 手势处理器。这也解释了为什么默认值能统一作用于底层视图未显式传值时手势对象自身的配置里已经内置了这些默认值。底层原理创建、更新与重挂载流程GestureDetector并非简单地把gesture渲染出来它背后有一套完整的“创建 → 挂载 → 更新 → 卸载”生命周期管理。理解这条链路有助于排查手势不响应、配置不生效等问题。1. Wrap自动处理collapsable并接管子组件 refWrap是GestureDetector渲染的容器组件。除了强制要求唯一子元素外它还会自动给子组件加上collapsable: false并把子组件的 ref 接管到handleChildRef上从而拿到真正的宿主视图实例见 Wrap.tsxoverride render() { // ... this.childRef child.props.ref as WrapRef; return React.cloneElement( child, { collapsable: false, ref: this.handleChildRef }, child.props.children ); }正如源码注释所写这一步的唯一目的就是确保子视图存在于原生视图层级中这样探测器才能找到正确的viewTag进行手势挂载。关于collapsable的详细说明见下文“注意事项”一节。2. attachHandlers创建原生手势处理器并建立关系在组件挂载后GestureDetector会通过attachHandlers完成真正的原生侧注册见 attachHandlers.ts调用gestureConfig.initialize()初始化手势对象在微任务queueMicrotask中调用gestureConfig.prepare()确保所有 ref 初始化完成后再提取handlerTag遍历gesturesToAttach逐个调用RNGestureHandlerModule.createGestureHandler(...)创建原生处理器并调用registerHandler注册到 JS 侧处理器注册表中在微任务中调用setGestureHandlerConfig(...)同步最新配置并通过configureRelations(handler.handlerTag, extractGestureRelations(handler))建立手势之间的组合关系这正是Race/Simultaneous/Exclusive在原生层的落地方式最后调用attachGestureHandler(handlerTag, viewTag, actionType)把处理器绑定到视图上。3. useDetectorUpdater视图变化时重挂载配置变化时只更新useDetectorUpdater提供了手势的增量更新逻辑见 useDetectorUpdater.tsconst viewTag findNodeHandle(resolveHostInstance(state.viewRef)) as number; const didUnderlyingViewChange viewTag ! state.previousViewTag; if (didUnderlyingViewChange || needsToReattach(preparedGesture, gesturesToAttach)) { dropHandlers(preparedGesture); attachHandlers({ ... }); // ... } else if (!skipConfigUpdate) { updateHandlers(preparedGesture, gestureConfig, gesturesToAttach); }如果底层视图变了例如条件渲染切换了目标视图会先dropHandlers卸载旧处理器再attachHandlers重新挂载如果视图没变但手势配置变了则走updateHandlers增量更新避免不必要的重建开销对应的卸载逻辑在 dropHandlers.ts 中实现组件卸载时也会自动执行见 index.tsx 的useIsomorphicLayoutEffect清理函数。这套机制保证了手势配置可以随 React 渲染周期动态更新同时把原生侧的重建成本降到最低。注意事项Remarks1.collapsable与视图折叠陷阱GestureDetector会使用其子树中的第一个原生视图来识别手势。但是如果这个视图仅仅用于包裹子元素它可能会被 Android 自动折叠collapsed从而从原生视图层级中消失。考虑下面的例子export default function Example() { const tap Gesture.Tap().onStart(() { console.log(tap); }); return ( GestureDetector gesture{tap} FunctionalComponent View style{styles.box} / /FunctionalComponent /GestureDetector ); } function FunctionalComponent(props) { return View collapsable{false}{props.children}/View; }如果我们把FunctionalComponent里的collapsable属性移除手势就会停止工作——因为手势被附加到了一个不在视图层级中的视图上。GestureDetector会自动为它的直接子组件加上collapsable: false正是上文Wrap中React.cloneElement(child, { collapsable: false, ... })的作用但面对更复杂的视图树它无法自动处理所有层级的折叠问题需要开发者自行在必要的分组视图上显式设置collapsable{false}。2. 同一手势实例不能复用于多个 GestureDetector不要在多个GestureDetector之间共享同一个手势实例。下面的写法会直接抛错export default function Example() { const pan Gesture.Pan(); return ( View GestureDetector gesture{pan} View GestureDetector gesture{pan} { } {/* Dont do this! */} View / /GestureDetector /View /GestureDetector /View ); }错误原因在于同一个Pan实例被同时用在两个GestureDetector中而手势对象的内部状态handlerTag、挂载标记等只能与一个检测器绑定。正确做法是为每个GestureDetector创建独立的手势实例即把Gesture.Pan()的调用放在需要它的组件内部例如通过useMemo或直接内联创建。此外gesture属性为必填项如果未提供gesture属性GestureDetector会直接抛出GestureDetector must have a gesture prop provided.错误这一点在仓库的测试用例 Errors.test.tsx 中也有覆盖。小结与最佳实践GestureDetector是 RNGH2 新 API 的入口组件负责把手势对象翻译成原生手势处理器并支持Race、Simultaneous、Exclusive组合手势需要同步手势处理时让手势回调成为 Reanimated workletGestureDetector会自动切换到 Reanimated 渲染路径Web 平台上可通过userSelect、touchAction、enableContextMenu控制底层视图的浏览器行为这三个配置会自动传播到组合手势的每一个基础手势目标视图务必保证存在于原生视图层级中善用collapsable{false}且一个手势实例只能挂载到一个GestureDetector上若需深入组合手势的语义可继续阅读仓库中的 composed-gestures.md以及 v2 文档下的 gesture.md、state-manager.md 等配套文档。赞分享移动开发UI组件【免费下载链接】react-native-gesture-handlerDeclarative API exposing platform native touch and gesture system to React Native.项目地址https://gitcode.com/gh_mirrors/re/react-native-gesture-handler点击查看免费下载相关推荐React Native Gesture Handler手势检测器GestureDetector的现代化API使用指南React Native Gesture Handler手势检测器GestureDetector的现代化API使用指南 React Native Gestur移动开发UI组件Apache Superset 3.1.0 版本技术解读新数据库支持、SQL Lab 体验升级与图表迁移体系Apache Superset 3.1.0 版本技术解读新数据库支持、SQL Lab 体验升级与图表迁移体系 Apache Superset 3.1.0发布移动开发UI组件pandas 2.3.3 版本解读首个全面兼容 Python 3.14 的发行版与 StringDtype 系列修复pandas 2.3.3 版本解读首个全面兼容 Python 3.14 的发行版与 StringDtype 系列修复 本篇文章以 pandas 官方发行说明移动开发UI组件上一篇Perlite安装完全手册从零开始部署你的笔记网站下一篇Easydict 从 Objective-C 到 Swift SwiftUI 的渐进式迁移路线图、已落地成果与冻结政策全解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表