feat: flutter增加长按功能
This commit is contained in:
@@ -202,6 +202,8 @@ class _ControllerHomeState extends State<ControllerHome> {
|
|||||||
ControlCommands.swipe(x1, y1, x2, y2, d)),
|
ControlCommands.swipe(x1, y1, x2, y2, d)),
|
||||||
onKey: (k, a) => _controller
|
onKey: (k, a) => _controller
|
||||||
?.sendControlCommand(ControlCommands.key(k, a)),
|
?.sendControlCommand(ControlCommands.key(k, a)),
|
||||||
|
onLongPress: (x, y) => _controller
|
||||||
|
?.sendControlCommand(ControlCommands.longPress(x, y)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -37,4 +37,12 @@ class ControlCommands {
|
|||||||
'keyCode': keyCode,
|
'keyCode': keyCode,
|
||||||
'keyAction': action,
|
'keyAction': action,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// 长按指令。坐标 x/y 为相对于屏幕的百分比(0.0 ~ 1.0)。
|
||||||
|
/// 对应 Android 端 RemoteTouchView.createLongPressCommand。
|
||||||
|
static String longPress(double x, double y) => jsonEncode({
|
||||||
|
'action': 'LONG_PRESS',
|
||||||
|
'x': x,
|
||||||
|
'y': y,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -10,6 +11,7 @@ import 'package:flutter/services.dart';
|
|||||||
///
|
///
|
||||||
/// - 短时间内位移很小的抬起 -> 单击(TOUCH)
|
/// - 短时间内位移很小的抬起 -> 单击(TOUCH)
|
||||||
/// - 否则 -> 滑动(SWIPE)
|
/// - 否则 -> 滑动(SWIPE)
|
||||||
|
/// - 按住不动超过阈值 -> 长按(LONG_PRESS)
|
||||||
/// 同时捕获物理按键事件(KEY),用于外接键盘 / 遥控器场景。
|
/// 同时捕获物理按键事件(KEY),用于外接键盘 / 遥控器场景。
|
||||||
///
|
///
|
||||||
/// 该控件完全基于 Flutter 框架实现,无需任何原生代码,可运行于
|
/// 该控件完全基于 Flutter 框架实现,无需任何原生代码,可运行于
|
||||||
@@ -24,12 +26,14 @@ class RemoteTouchView extends StatefulWidget {
|
|||||||
int durationMs,
|
int durationMs,
|
||||||
) onSwipe;
|
) onSwipe;
|
||||||
final void Function(int keyCode, int action) onKey;
|
final void Function(int keyCode, int action) onKey;
|
||||||
|
final void Function(double x, double y) onLongPress;
|
||||||
|
|
||||||
const RemoteTouchView({
|
const RemoteTouchView({
|
||||||
super.key,
|
super.key,
|
||||||
required this.onTouch,
|
required this.onTouch,
|
||||||
required this.onSwipe,
|
required this.onSwipe,
|
||||||
required this.onKey,
|
required this.onKey,
|
||||||
|
required this.onLongPress,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -38,7 +42,16 @@ class RemoteTouchView extends StatefulWidget {
|
|||||||
|
|
||||||
class _RemoteTouchViewState extends State<RemoteTouchView> {
|
class _RemoteTouchViewState extends State<RemoteTouchView> {
|
||||||
Offset? _startPosition;
|
Offset? _startPosition;
|
||||||
|
Offset? _currentPosition;
|
||||||
DateTime? _startTime;
|
DateTime? _startTime;
|
||||||
|
bool _isLongPressed = false;
|
||||||
|
Timer? _longPressTimer;
|
||||||
|
|
||||||
|
/// 长按触发阈值,对应 Android GestureDetector 默认的 LONG_PRESS_TIMEOUT(400ms)。
|
||||||
|
static const _longPressTimeout = Duration(milliseconds: 400);
|
||||||
|
|
||||||
|
/// 手指移动超过该像素阈值则视为滚动,取消长按(对应 Android touch slop)。
|
||||||
|
static const _touchSlop = 10.0;
|
||||||
|
|
||||||
double _toRelativeX(Offset local, Size size) =>
|
double _toRelativeX(Offset local, Size size) =>
|
||||||
(local.dx / size.width).clamp(0.0, 1.0);
|
(local.dx / size.width).clamp(0.0, 1.0);
|
||||||
@@ -46,6 +59,17 @@ class _RemoteTouchViewState extends State<RemoteTouchView> {
|
|||||||
double _toRelativeY(Offset local, Size size) =>
|
double _toRelativeY(Offset local, Size size) =>
|
||||||
(local.dy / size.height).clamp(0.0, 1.0);
|
(local.dy / size.height).clamp(0.0, 1.0);
|
||||||
|
|
||||||
|
void _cancelLongPress() {
|
||||||
|
_longPressTimer?.cancel();
|
||||||
|
_longPressTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_cancelLongPress();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Focus(
|
return Focus(
|
||||||
@@ -64,10 +88,39 @@ class _RemoteTouchViewState extends State<RemoteTouchView> {
|
|||||||
child: Listener(
|
child: Listener(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
onPointerDown: (event) {
|
onPointerDown: (event) {
|
||||||
|
_cancelLongPress();
|
||||||
|
_isLongPressed = false;
|
||||||
_startPosition = event.localPosition;
|
_startPosition = event.localPosition;
|
||||||
|
_currentPosition = event.localPosition;
|
||||||
_startTime = DateTime.now();
|
_startTime = DateTime.now();
|
||||||
|
_longPressTimer = Timer(_longPressTimeout, () {
|
||||||
|
final size = context.size;
|
||||||
|
if (_currentPosition == null || size == null) return;
|
||||||
|
_isLongPressed = true;
|
||||||
|
final x = _toRelativeX(_currentPosition!, size);
|
||||||
|
final y = _toRelativeY(_currentPosition!, size);
|
||||||
|
widget.onLongPress(x, y);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onPointerMove: (event) {
|
||||||
|
_currentPosition = event.localPosition;
|
||||||
|
if (_longPressTimer != null && _startPosition != null) {
|
||||||
|
final dx = (event.localPosition.dx - _startPosition!.dx).abs();
|
||||||
|
final dy = (event.localPosition.dy - _startPosition!.dy).abs();
|
||||||
|
if (dx > _touchSlop || dy > _touchSlop) {
|
||||||
|
_cancelLongPress();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onPointerUp: (event) {
|
onPointerUp: (event) {
|
||||||
|
_cancelLongPress();
|
||||||
|
// 长按已触发则不再派发单击 / 滑动事件(对应 Android 端 isLongPressed 逻辑)。
|
||||||
|
if (_isLongPressed) {
|
||||||
|
_startPosition = null;
|
||||||
|
_currentPosition = null;
|
||||||
|
_startTime = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
final size = context.size;
|
final size = context.size;
|
||||||
if (_startPosition == null || size == null || _startTime == null) {
|
if (_startPosition == null || size == null || _startTime == null) {
|
||||||
return;
|
return;
|
||||||
@@ -87,6 +140,7 @@ class _RemoteTouchViewState extends State<RemoteTouchView> {
|
|||||||
widget.onSwipe(startX, startY, endX, endY, max(duration, 1));
|
widget.onSwipe(startX, startY, endX, endY, max(duration, 1));
|
||||||
}
|
}
|
||||||
_startPosition = null;
|
_startPosition = null;
|
||||||
|
_currentPosition = null;
|
||||||
_startTime = null;
|
_startTime = null;
|
||||||
},
|
},
|
||||||
child: const ColoredBox(
|
child: const ColoredBox(
|
||||||
|
|||||||
Reference in New Issue
Block a user