Flutter已经包含
onSecondaryTap
onTertiaryTap
onDoubleTap
Flutter是否也包含鼠标右键或中键双击检测的属性?
dm7nw8vv1#
Flutter中的内置手势检测器不包含onDoubleSecondary tap或onDoubleTertiaryTap的属性,但您可以创建自己的手势识别器并将其添加到原始手势检测器。下面是一个识别双击右键的GestureRecognizer:
class _CountdownZoned { _CountdownZoned({ required Duration duration }) : assert(duration != null) { Timer(duration, _onTimeout); } bool _timeout = false; bool get timeout => _timeout; void _onTimeout() { _timeout = true; } } class _TapTracker { _TapTracker({ required PointerDownEvent event, required this.entry, required Duration doubleTapMinTime, required this.gestureSettings, }) : assert(doubleTapMinTime != null), assert(event != null), assert(event.buttons != null), pointer = event.pointer, _initialGlobalPosition = event.position, initialButtons = event.buttons, _doubleTapMinTimeCountdown = _CountdownZoned(duration: doubleTapMinTime); final DeviceGestureSettings? gestureSettings; final int pointer; final GestureArenaEntry entry; final Offset _initialGlobalPosition; final int initialButtons; final _CountdownZoned _doubleTapMinTimeCountdown; bool _isTrackingPointer = false; void startTrackingPointer(PointerRoute route, Matrix4? transform) { if (!_isTrackingPointer) { _isTrackingPointer = true; GestureBinding.instance.pointerRouter.addRoute(pointer, route, transform); } } void stopTrackingPointer(PointerRoute route) { if (_isTrackingPointer) { _isTrackingPointer = false; GestureBinding.instance.pointerRouter.removeRoute(pointer, route); } } bool isWithinGlobalTolerance(PointerEvent event, double tolerance) { final Offset offset = event.position - _initialGlobalPosition; return offset.distance <= tolerance; } bool hasElapsedMinTime() { return _doubleTapMinTimeCountdown.timeout; } bool hasSameButton(PointerDownEvent event) { return event.buttons == initialButtons; } } class DoubleRightClick extends GestureRecognizer { DoubleRightClick({ super.debugOwner, @Deprecated( 'Migrate to supportedDevices. ' 'This feature was deprecated after v2.3.0-1.0.pre.', ) super.kind, super.supportedDevices, }); GestureTapDownCallback? onDoubleTapDown; GestureDoubleTapCallback? onDoubleTap; GestureTapCancelCallback? onDoubleTapCancel; Timer? _doubleTapTimer; _TapTracker? _firstTap; final Map<int, _TapTracker> _trackers = <int, _TapTracker>{}; @override bool isPointerAllowed(PointerDownEvent event) { if (_firstTap == null) { switch (event.buttons) { case kSecondaryMouseButton: if (onDoubleTapDown == null && onDoubleTap == null && onDoubleTapCancel == null) { return false; } break; default: return false; } } return super.isPointerAllowed(event); } @override void addAllowedPointer(PointerDownEvent event) { if (_firstTap != null) { if (!_firstTap!.isWithinGlobalTolerance(event, kDoubleTapSlop)) { // Ignore out-of-bounds second taps. return; } else if (!_firstTap!.hasElapsedMinTime() || !_firstTap!.hasSameButton(event)) { // Restart when the second tap is too close to the first (touch screens // often detect touches intermittently), or when buttons mismatch. _reset(); return _trackTap(event); } else if (onDoubleTapDown != null) { final TapDownDetails details = TapDownDetails( globalPosition: event.position, localPosition: event.localPosition, kind: getKindForPointer(event.pointer), ); invokeCallback<void>('onDoubleTapDown', () => onDoubleTapDown!(details)); } } _trackTap(event); } void _trackTap(PointerDownEvent event) { _stopDoubleTapTimer(); final _TapTracker tracker = _TapTracker( event: event, entry: GestureBinding.instance.gestureArena.add(event.pointer, this), doubleTapMinTime: kDoubleTapMinTime, gestureSettings: gestureSettings, ); _trackers[event.pointer] = tracker; tracker.startTrackingPointer(_handleEvent, event.transform); } void _handleEvent(PointerEvent event) { final _TapTracker tracker = _trackers[event.pointer]!; if (event is PointerUpEvent) { if (_firstTap == null) { _registerFirstTap(tracker); } else { _registerSecondTap(tracker); } } else if (event is PointerMoveEvent) { if (!tracker.isWithinGlobalTolerance(event, kDoubleTapTouchSlop)) { _reject(tracker); } } else if (event is PointerCancelEvent) { _reject(tracker); } } @override void acceptGesture(int pointer) { } @override void rejectGesture(int pointer) { _TapTracker? tracker = _trackers[pointer]; // If tracker isn't in the list, check if this is the first tap tracker if (tracker == null && _firstTap != null && _firstTap!.pointer == pointer) { tracker = _firstTap; } // If tracker is still null, we rejected ourselves already if (tracker != null) { _reject(tracker); } } void _reject(_TapTracker tracker) { _trackers.remove(tracker.pointer); tracker.entry.resolve(GestureDisposition.rejected); _freezeTracker(tracker); if (_firstTap != null) { if (tracker == _firstTap) { _reset(); } else { _checkCancel(); if (_trackers.isEmpty) { _reset(); } } } } @override void dispose() { _reset(); super.dispose(); } void _reset() { _stopDoubleTapTimer(); if (_firstTap != null) { if (_trackers.isNotEmpty) { _checkCancel(); } // Note, order is important below in order for the resolve -> reject logic // to work properly. final _TapTracker tracker = _firstTap!; _firstTap = null; _reject(tracker); GestureBinding.instance.gestureArena.release(tracker.pointer); } _clearTrackers(); } void _registerFirstTap(_TapTracker tracker) { _startDoubleTapTimer(); GestureBinding.instance.gestureArena.hold(tracker.pointer); // Note, order is important below in order for the clear -> reject logic to // work properly. _freezeTracker(tracker); _trackers.remove(tracker.pointer); _clearTrackers(); _firstTap = tracker; } void _registerSecondTap(_TapTracker tracker) { _firstTap!.entry.resolve(GestureDisposition.accepted); tracker.entry.resolve(GestureDisposition.accepted); _freezeTracker(tracker); _trackers.remove(tracker.pointer); _checkUp(tracker.initialButtons); _reset(); } void _clearTrackers() { _trackers.values.toList().forEach(_reject); assert(_trackers.isEmpty); } void _freezeTracker(_TapTracker tracker) { tracker.stopTrackingPointer(_handleEvent); } void _startDoubleTapTimer() { _doubleTapTimer ??= Timer(kDoubleTapTimeout, _reset); } void _stopDoubleTapTimer() { if (_doubleTapTimer != null) { _doubleTapTimer!.cancel(); _doubleTapTimer = null; } } void _checkUp(int buttons) { assert(buttons == kSecondaryMouseButton); if (onDoubleTap != null) { invokeCallback<void>('onDoubleTap', onDoubleTap!); } } void _checkCancel() { if (onDoubleTapCancel != null) { invokeCallback<void>('onDoubleTapCancel', onDoubleTapCancel!); } } @override String get debugDescription => 'double tap'; }
你可以这样使用它:
RawGestureDetector( gestures: { DoubleRightClick: GestureRecognizerFactoryWithHandlers<DoubleRightClick>( () => DoubleRightClick(), (DoubleRightClick instance) { instance.onDoubleTap = () { debugPrint('Double Right Click'); }; }, ), }, child: Container( width: 200, height: 200, color: Colors.red, child: SizedBox( width: 200, height: 200, child: FlutterLogo(), ), ), ),
这篇来自Flutter团队的YouTube video解释了Gesture竞技场的工作原理。
1条答案
按热度按时间dm7nw8vv1#
Flutter中的内置手势检测器不包含onDoubleSecondary tap或onDoubleTertiaryTap的属性,但您可以创建自己的手势识别器并将其添加到原始手势检测器。
下面是一个识别双击右键的GestureRecognizer:
你可以这样使用它:
这篇来自Flutter团队的YouTube video解释了Gesture竞技场的工作原理。