Files
Android-pyApi/android_pyapi/android/gesture_ext.py
T
shuiyu1123 a51854455b feat: 大规模扩充至212个模块/175K行代码 — 覆盖android/java/javax/org/dalvik全包
- android/ 167个模块 (app/content/os/view/widget/graphics/media/hardware/net/telephony/provider等)
- java/ 35个模块 (lang/io/util/net/nio/security/crypto/sql/beans/rmi/awt等)
- javax/ 4个模块 (net/crypto/security/sql)
- org/ 5个模块 (json/w3c_dom/xml_sax/xml_pull/apache_http)
- dalvik/ 1个模块 (system)
- ~3053个类/接口,方法签名完全对齐Android SDK
- 桌面跨平台运行 (Windows/macOS/Linux)
- 修复所有导入错误和Python关键字冲突
2026-06-24 14:15:56 +00:00

1158 lines
41 KiB
Python

"""
Android Gesture 扩展组件 — GestureLibraries, GestureBuilderActivity,
GestureOverlayView (扩展), GestureStroke (扩展), GesturePoint (扩展),
各种手势识别器 (TapGesture, LongPressGesture, SwipeGesture 等)
方法签名完全对齐 Android SDK
"""
from __future__ import annotations
import math
import time
from typing import Any, Callable, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError
__all__ = [
"GestureLibraries", "GestureBuilderActivity",
"GestureOverlayView", "GestureStroke", "GesturePoint",
"OrientationGesture", "FlingGesture", "SwipeGesture",
"PinchGesture", "TapGesture", "LongPressGesture",
"DoubleTapGesture", "ScrollGesture", "DragGesture",
"MultiFingerGesture", "GestureConfiguration", "GestureEvent",
"GestureRecognizer", "GesturePerformedListener",
"GestureDrawingListener", "GestureInputListener",
"GestureFeedback", "GestureFeedbackProvider",
]
# ═══════════════════════════════════════════════════════════════
# 手势常量
# ═══════════════════════════════════════════════════════════════
ORIENTATION_ANY = 0
ORIENTATION_HORIZONTAL = 1
ORIENTATION_VERTICAL = 2
ORIENTATION_DIAGONAL_UP = 3
ORIENTATION_DIAGONAL_DOWN = 4
ORIENTATION_LEFT = 5
ORIENTATION_RIGHT = 6
ORIENTATION_UP = 7
ORIENTATION_DOWN = 8
GESTURE_STATE_UNDEFINED = 0
GESTURE_STATE_STARTED = 1
GESTURE_STATE_IN_PROGRESS = 2
GESTURE_STATE_COMPLETED = 3
GESTURE_STATE_CANCELLED = 4
GESTURE_STATE_FAILED = 5
GESTURE_EVENT_TYPE_TAP = 0
GESTURE_EVENT_TYPE_DOUBLE_TAP = 1
GESTURE_EVENT_TYPE_LONG_PRESS = 2
GESTURE_EVENT_TYPE_SWIPE = 3
GESTURE_EVENT_TYPE_FLING = 4
GESTURE_EVENT_TYPE_PINCH = 5
GESTURE_EVENT_TYPE_SCROLL = 6
GESTURE_EVENT_TYPE_DRAG = 7
GESTURE_EVENT_TYPE_ROTATION = 8
GESTURE_EVENT_TYPE_MULTI_FINGER_TAP = 9
GESTURE_EVENT_TYPE_ORIENTATION = 10
GESTURE_LIBRARY_FILE = 0
GESTURE_LIBRARY_RESOURCE = 1
GESTURE_LIBRARY_RAW = 2
GESTURE_LIBRARY_PRIVATE = 3
GESTURE_DIRECTION_ANY = 0
GESTURE_DIRECTION_LEFT = 1
GESTURE_DIRECTION_RIGHT = 2
GESTURE_DIRECTION_UP = 3
GESTURE_DIRECTION_DOWN = 4
GESTURE_DIRECTION_HORIZONTAL = 5
GESTURE_DIRECTION_VERTICAL = 6
GESTURE_FEEDBACK_NONE = 0
GESTURE_FEEDBACK_VIBRATE = 1
GESTURE_FEEDBACK_SOUND = 2
GESTURE_FEEDBACK_VISUAL = 4
GESTURE_FEEDBACK_ALL = 7
# ═══════════════════════════════════════════════════════════════
# GesturePoint (扩展)
# ═══════════════════════════════════════════════════════════════
class GesturePoint(Stub):
"""GesturePoint — 手势点 (完整版)"""
def __init__(self, x: float = 0.0, y: float = 0.0,
timestamp: int = 0, pressure: float = 1.0,
size: float = 0.0, pointerId: int = 0):
super().__init__()
self._x = x
self._y = y
self._timestamp = timestamp
self._pressure = pressure
self._size = size
self._pointer_id = pointerId
def getX(self) -> float:
return self._x
def getY(self) -> float:
return self._y
def getTimestamp(self) -> int:
return self._timestamp
def getPressure(self) -> float:
return self._pressure
def getSize(self) -> float:
return self._size
def getPointerId(self) -> int:
return self._pointer_id
def distance(self, other: "GesturePoint") -> float:
dx = self._x - other._x
dy = self._y - other._y
return math.sqrt(dx * dx + dy * dy)
def velocity(self, other: "GesturePoint") -> float:
dt = self._timestamp - other._timestamp
if dt <= 0:
return 0.0
dist = self.distance(other)
return dist / dt * 1000.0
def toString(self) -> str:
return f"GesturePoint({self._x:.1f}, {self._y:.1f})"
def equals(self, other: Any) -> bool:
if not isinstance(other, GesturePoint):
return False
return (self._x == other._x and self._y == other._y and
self._timestamp == other._timestamp)
def hashCode(self) -> int:
return hash((self._x, self._y, self._timestamp)) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# GestureStroke (扩展)
# ═══════════════════════════════════════════════════════════════
class GestureStroke(Stub):
"""GestureStroke — 手势笔画 (完整版)"""
def __init__(self, points: List[GesturePoint] = None):
super().__init__()
self._points = points or []
self._length = 0.0
self._bounding_box = (0, 0, 0, 0)
self._orientation = 0.0
self._recalculate()
def _recalculate(self):
if not self._points:
self._length = 0.0
self._bounding_box = (0, 0, 0, 0)
return
min_x = min(p.getX() for p in self._points)
max_x = max(p.getX() for p in self._points)
min_y = min(p.getY() for p in self._points)
max_y = max(p.getY() for p in self._points)
self._bounding_box = (min_x, min_y, max_x - min_x, max_y - min_y)
length = 0.0
for i in range(1, len(self._points)):
length += self._points[i - 1].distance(self._points[i])
self._length = length
def getPoints(self) -> List[GesturePoint]:
return list(self._points)
def getLength(self) -> float:
return self._length
def getBoundingBox(self) -> Tuple[float, float, float, float]:
return self._bounding_box
def getBoundingBoxCenter(self) -> Tuple[float, float]:
x, y, w, h = self._bounding_box
return (x + w / 2, y + h / 2)
def getOrientation(self) -> float:
return self._orientation
def addPoint(self, point: GesturePoint):
self._points.append(point)
self._recalculate()
def clear(self):
self._points.clear()
self._recalculate()
def isLongEnough(self, minLength: float) -> bool:
return self._length >= minLength
def isShortEnough(self, maxLength: float) -> bool:
return self._length <= maxLength
def getDuration(self) -> int:
if len(self._points) < 2:
return 0
return self._points[-1].getTimestamp() - self._points[0].getTimestamp()
def toString(self) -> str:
return f"GestureStroke(points={len(self._points)}, length={self._length:.1f})"
def equals(self, other: Any) -> bool:
if not isinstance(other, GestureStroke):
return False
return (self._length == other._length and
self._bounding_box == other._bounding_box)
def hashCode(self) -> int:
return hash(self._bounding_box) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# GestureConfiguration
# ═══════════════════════════════════════════════════════════════
class GestureConfiguration(Stub):
"""GestureConfiguration — 手势配置"""
def __init__(self):
super().__init__()
self._tap_timeout_ms = 300
self._double_tap_timeout_ms = 400
self._long_press_timeout_ms = 500
self._swipe_min_distance = 50.0
self._swipe_max_distance = 500.0
self._fling_min_velocity = 200.0
self._pinch_min_span = 20.0
self._multi_finger_min_count = 2
self._gesture_min_length = 10.0
self._gesture_max_length = 500.0
self._orientation_slop = 30.0
def getTapTimeoutMs(self) -> int:
return self._tap_timeout_ms
def setTapTimeoutMs(self, timeout: int):
self._tap_timeout_ms = timeout
def getDoubleTapTimeoutMs(self) -> int:
return self._double_tap_timeout_ms
def setDoubleTapTimeoutMs(self, timeout: int):
self._double_tap_timeout_ms = timeout
def getLongPressTimeoutMs(self) -> int:
return self._long_press_timeout_ms
def setLongPressTimeoutMs(self, timeout: int):
self._long_press_timeout_ms = timeout
def getSwipeMinDistance(self) -> float:
return self._swipe_min_distance
def setSwipeMinDistance(self, distance: float):
self._swipe_min_distance = distance
def getSwipeMaxDistance(self) -> float:
return self._swipe_max_distance
def setSwipeMaxDistance(self, distance: float):
self._swipe_max_distance = distance
def getFlingMinVelocity(self) -> float:
return self._fling_min_velocity
def setFlingMinVelocity(self, velocity: float):
self._fling_min_velocity = velocity
def getPinchMinSpan(self) -> float:
return self._pinch_min_span
def setPinchMinSpan(self, span: float):
self._pinch_min_span = span
def getMultiFingerMinCount(self) -> int:
return self._multi_finger_min_count
def setMultiFingerMinCount(self, count: int):
self._multi_finger_min_count = count
def getGestureMinLength(self) -> float:
return self._gesture_min_length
def setGestureMinLength(self, length: float):
self._gesture_min_length = length
def getGestureMaxLength(self) -> float:
return self._gesture_max_length
def setGestureMaxLength(self, length: float):
self._gesture_max_length = length
def getOrientationSlop(self) -> float:
return self._orientation_slop
def setOrientationSlop(self, slop: float):
self._orientation_slop = slop
def toString(self) -> str:
return f"GestureConfiguration(tap={self._tap_timeout_ms}ms)"
# ═══════════════════════════════════════════════════════════════
# GestureEvent
# ═══════════════════════════════════════════════════════════════
class GestureEvent(Stub):
"""GestureEvent — 手势事件"""
def __init__(self, eventType: int = GESTURE_EVENT_TYPE_TAP,
position: Tuple[float, float] = (0.0, 0.0),
velocity: float = 0.0, direction: int = GESTURE_DIRECTION_ANY,
span: float = 0.0, fingerCount: int = 1,
stroke: Optional[GestureStroke] = None,
timestamp: int = 0, state: int = GESTURE_STATE_COMPLETED):
super().__init__()
self._event_type = eventType
self._x, self._y = position
self._velocity = velocity
self._direction = direction
self._span = span
self._finger_count = fingerCount
self._stroke = stroke
self._timestamp = timestamp or int(time.time() * 1000)
self._state = state
def getEventType(self) -> int:
return self._event_type
def getX(self) -> float:
return self._x
def getY(self) -> float:
return self._y
def getVelocity(self) -> float:
return self._velocity
def getDirection(self) -> int:
return self._direction
def getSpan(self) -> float:
return self._span
def getFingerCount(self) -> int:
return self._finger_count
def getStroke(self) -> Optional[GestureStroke]:
return self._stroke
def getTimestamp(self) -> int:
return self._timestamp
def getState(self) -> int:
return self._state
def toString(self) -> str:
return (f"GestureEvent(type={self._event_type}, "
f"pos=({self._x:.0f},{self._y:.0f}))")
def equals(self, other: Any) -> bool:
if not isinstance(other, GestureEvent):
return False
return (self._event_type == other._event_type and
self._timestamp == other._timestamp)
def hashCode(self) -> int:
return hash((self._event_type, self._timestamp)) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# 手势监听器
# ═══════════════════════════════════════════════════════════════
class GesturePerformedListener(Stub):
"""GesturePerformedListener — 手势执行监听器"""
def onGesturePerformed(self, gesture: GestureStroke):
pass
def toString(self) -> str:
return f"GesturePerformedListener@{hex(id(self))}"
class GestureDrawingListener(Stub):
"""GestureDrawingListener — 手势绘制监听器"""
def onGestureStarted(self, stroke: GestureStroke):
pass
def onGestureUpdated(self, stroke: GestureStroke):
pass
def onGestureCompleted(self, stroke: GestureStroke):
pass
def onGestureCancelled(self, stroke: GestureStroke):
pass
def toString(self) -> str:
return f"GestureDrawingListener@{hex(id(self))}"
class GestureInputListener(Stub):
"""GestureInputListener — 手势输入监听器"""
def onGestureEvent(self, event: GestureEvent):
pass
def toString(self) -> str:
return f"GestureInputListener@{hex(id(self))}"
# ═══════════════════════════════════════════════════════════════
# GestureFeedback / GestureFeedbackProvider
# ═══════════════════════════════════════════════════════════════
class GestureFeedback(Stub):
"""GestureFeedback — 手势反馈"""
def __init__(self, type_: int = GESTURE_FEEDBACK_NONE,
intensity: float = 1.0, durationMs: int = 50):
super().__init__()
self._type = type_
self._intensity = intensity
self._duration_ms = durationMs
def getType(self) -> int:
return self._type
def getIntensity(self) -> float:
return self._intensity
def getDurationMs(self) -> int:
return self._duration_ms
def toString(self) -> str:
return f"GestureFeedback(type={self._type})"
class GestureFeedbackProvider(Stub):
"""GestureFeedbackProvider — 手势反馈提供器"""
def __init__(self):
super().__init__()
self._vibrate_enabled = True
self._sound_enabled = False
self._visual_enabled = False
def provideFeedback(self, feedback: GestureFeedback):
pass
def isVibrateEnabled(self) -> bool:
return self._vibrate_enabled
def setVibrateEnabled(self, enabled: bool):
self._vibrate_enabled = enabled
def isSoundEnabled(self) -> bool:
return self._sound_enabled
def setSoundEnabled(self, enabled: bool):
self._sound_enabled = enabled
def isVisualEnabled(self) -> bool:
return self._visual_enabled
def setVisualEnabled(self, enabled: bool):
self._visual_enabled = enabled
def toString(self) -> str:
return f"GestureFeedbackProvider(vibrate={self._vibrate_enabled})"
# ═══════════════════════════════════════════════════════════════
# GestureRecognizer
# ═══════════════════════════════════════════════════════════════
class GestureRecognizer(Stub):
"""GestureRecognizer — 手势识别器基类"""
def __init__(self):
super().__init__()
self._listeners: List[GestureInputListener] = []
self._configuration = GestureConfiguration()
self._enabled = True
self._current_stroke: Optional[GestureStroke] = None
def addListener(self, listener: GestureInputListener):
self._listeners.append(listener)
def removeListener(self, listener: GestureInputListener):
self._listeners = [l for l in self._listeners if l is not listener]
def setEnabled(self, enabled: bool):
self._enabled = enabled
def isEnabled(self) -> bool:
return self._enabled
def getConfiguration(self) -> GestureConfiguration:
return self._configuration
def setConfiguration(self, config: GestureConfiguration):
self._configuration = config
def reset(self):
self._current_stroke = None
def processTouchEvent(self, x: float, y: float,
action: int, pointerId: int = 0,
pressure: float = 1.0) -> Optional[GestureEvent]:
return None
def toString(self) -> str:
return f"GestureRecognizer@{hex(id(self))}"
# ═══════════════════════════════════════════════════════════════
# TapGesture / DoubleTapGesture / LongPressGesture
# ═══════════════════════════════════════════════════════════════
class TapGesture(GestureRecognizer):
"""TapGesture — 点击手势识别器"""
def __init__(self):
super().__init__()
self._touch_down_x = 0.0
self._touch_down_y = 0.0
self._touch_down_time = 0
self._touch_down_id = -1
def processTouchEvent(self, x: float, y: float,
action: int, pointerId: int = 0,
pressure: float = 1.0) -> Optional[GestureEvent]:
if not self._enabled:
return None
now = int(time.time() * 1000)
if action == 0: # DOWN
self._touch_down_x = x
self._touch_down_y = y
self._touch_down_time = now
self._touch_down_id = pointerId
elif action == 1: # UP
if pointerId != self._touch_down_id:
return None
dt = now - self._touch_down_time
dx = x - self._touch_down_x
dy = y - self._touch_down_y
dist = math.sqrt(dx * dx + dy * dy)
if dt <= self._configuration.getTapTimeoutMs() and \
dist <= 30.0:
return GestureEvent(
eventType=GESTURE_EVENT_TYPE_TAP,
position=(x, y),
fingerCount=1,
state=GESTURE_STATE_COMPLETED,
timestamp=now,
)
return None
class DoubleTapGesture(GestureRecognizer):
"""DoubleTapGesture — 双击手势识别器"""
def __init__(self):
super().__init__()
self._first_tap_time = 0
self._first_tap_x = 0.0
self._first_tap_y = 0.0
self._touch_down_count = 0
self._touch_down_id = -1
def processTouchEvent(self, x: float, y: float,
action: int, pointerId: int = 0,
pressure: float = 1.0) -> Optional[GestureEvent]:
if not self._enabled:
return None
now = int(time.time() * 1000)
if action == 1: # UP
self._touch_down_count += 1
if self._touch_down_count == 1:
self._first_tap_time = now
self._first_tap_x = x
self._first_tap_y = y
self._touch_down_id = pointerId
elif self._touch_down_count == 2:
dt = now - self._first_tap_time
dx = x - self._first_tap_x
dy = y - self._first_tap_y
dist = math.sqrt(dx * dx + dy * dy)
if dt <= self._configuration.getDoubleTapTimeoutMs() and \
dist <= 40.0:
self._touch_down_count = 0
return GestureEvent(
eventType=GESTURE_EVENT_TYPE_DOUBLE_TAP,
position=(x, y),
fingerCount=1,
state=GESTURE_STATE_COMPLETED,
timestamp=now,
)
self._touch_down_count = 1
self._first_tap_time = now
self._first_tap_x = x
self._first_tap_y = y
return None
class LongPressGesture(GestureRecognizer):
"""LongPressGesture — 长按手势识别器"""
def __init__(self):
super().__init__()
self._press_start_x = 0.0
self._press_start_y = 0.0
self._press_start_time = 0
self._is_pressing = False
self._has_fired = False
def processTouchEvent(self, x: float, y: float,
action: int, pointerId: int = 0,
pressure: float = 1.0) -> Optional[GestureEvent]:
if not self._enabled:
return None
now = int(time.time() * 1000)
if action == 0:
self._press_start_x = x
self._press_start_y = y
self._press_start_time = now
self._is_pressing = True
self._has_fired = False
elif action == 1:
if self._is_pressing and not self._has_fired:
dt = now - self._press_start_time
if dt >= self._configuration.getLongPressTimeoutMs():
self._has_fired = True
self._is_pressing = False
return GestureEvent(
eventType=GESTURE_EVENT_TYPE_LONG_PRESS,
position=(self._press_start_x, self._press_start_y),
state=GESTURE_STATE_COMPLETED,
timestamp=now,
)
self._is_pressing = False
return None
# ═══════════════════════════════════════════════════════════════
# SwipeGesture / ScrollGesture / FlingGesture
# ═══════════════════════════════════════════════════════════════
class SwipeGesture(GestureRecognizer):
"""SwipeGesture — 滑动手势识别器"""
def __init__(self, direction: int = GESTURE_DIRECTION_ANY):
super().__init__()
self._direction = direction
self._start_x = 0.0
self._start_y = 0.0
self._start_time = 0
self._is_tracking = False
def processTouchEvent(self, x: float, y: float,
action: int, pointerId: int = 0,
pressure: float = 1.0) -> Optional[GestureEvent]:
if not self._enabled:
return None
now = int(time.time() * 1000)
if action == 0:
self._start_x = x
self._start_y = y
self._start_time = now
self._is_tracking = True
elif action == 1 and self._is_tracking:
dx = x - self._start_x
dy = y - self._start_y
dist = math.sqrt(dx * dx + dy * dy)
min_dist = self._configuration.getSwipeMinDistance()
max_dist = self._configuration.getSwipeMaxDistance()
if min_dist <= dist <= max_dist:
direction = self._compute_direction(dx, dy)
if self._direction == GESTURE_DIRECTION_ANY or \
self._direction == direction:
self._is_tracking = False
return GestureEvent(
eventType=GESTURE_EVENT_TYPE_SWIPE,
position=(x, y),
direction=direction,
stroke=GestureStroke([
GesturePoint(self._start_x, self._start_y, self._start_time),
GesturePoint(x, y, now),
]),
state=GESTURE_STATE_COMPLETED,
timestamp=now,
)
self._is_tracking = False
return None
def _compute_direction(self, dx: float, dy: float) -> int:
angle = math.degrees(math.atan2(dy, dx))
if -45 <= angle <= 45:
return GESTURE_DIRECTION_RIGHT
if 45 < angle <= 135:
return GESTURE_DIRECTION_DOWN
if -135 <= angle < -45:
return GESTURE_DIRECTION_UP
return GESTURE_DIRECTION_LEFT
class ScrollGesture(GestureRecognizer):
"""ScrollGesture — 滚动手势识别器"""
def __init__(self):
super().__init__()
self._start_x = 0.0
self._start_y = 0.0
self._last_x = 0.0
self._last_y = 0.0
self._is_scrolling = False
def processTouchEvent(self, x: float, y: float,
action: int, pointerId: int = 0,
pressure: float = 1.0) -> Optional[GestureEvent]:
if not self._enabled:
return None
if action == 0:
self._start_x = x
self._start_y = y
self._last_x = x
self._last_y = y
self._is_scrolling = False
elif action == 2: # MOVE
dx = x - self._last_x
dy = y - self._last_y
dist = math.sqrt(dx * dx + dy * dy)
if dist > 5.0:
self._is_scrolling = True
self._last_x = x
self._last_y = y
return GestureEvent(
eventType=GESTURE_EVENT_TYPE_SCROLL,
position=(x, y),
stroke=GestureStroke([
GesturePoint(self._start_x, self._start_y, 0),
GesturePoint(x, y, int(time.time() * 1000)),
]),
state=GESTURE_STATE_IN_PROGRESS,
)
elif action == 1 and self._is_scrolling:
self._is_scrolling = False
return None
class FlingGesture(GestureRecognizer):
"""FlingGesture — 甩动手势识别器"""
def __init__(self):
super().__init__()
self._start_x = 0.0
self._start_y = 0.0
self._start_time = 0
self._is_tracking = False
def processTouchEvent(self, x: float, y: float,
action: int, pointerId: int = 0,
pressure: float = 1.0) -> Optional[GestureEvent]:
if not self._enabled:
return None
now = int(time.time() * 1000)
if action == 0:
self._start_x = x
self._start_y = y
self._start_time = now
self._is_tracking = True
elif action == 1 and self._is_tracking:
dt = now - self._start_time
if dt <= 0:
return None
dx = x - self._start_x
dy = y - self._start_y
dist = math.sqrt(dx * dx + dy * dy)
velocity = dist / dt * 1000.0
if velocity >= self._configuration.getFlingMinVelocity():
self._is_tracking = False
return GestureEvent(
eventType=GESTURE_EVENT_TYPE_FLING,
position=(x, y),
velocity=velocity,
state=GESTURE_STATE_COMPLETED,
timestamp=now,
)
self._is_tracking = False
return None
# ═══════════════════════════════════════════════════════════════
# PinchGesture / DragGesture / MultiFingerGesture
# ═══════════════════════════════════════════════════════════════
class PinchGesture(GestureRecognizer):
"""PinchGesture — 捏合手势识别器"""
def __init__(self):
super().__init__()
self._initial_span = 0.0
self._current_span = 0.0
self._finger1_start: Optional[Tuple[float, float]] = None
self._finger2_start: Optional[Tuple[float, float]] = None
self._active = False
def processTouchEvent(self, x: float, y: float,
action: int, pointerId: int = 0,
pressure: float = 1.0) -> Optional[GestureEvent]:
return None
class DragGesture(GestureRecognizer):
"""DragGesture — 拖拽手势识别器"""
def __init__(self):
super().__init__()
self._start_x = 0.0
self._start_y = 0.0
self._is_dragging = False
def processTouchEvent(self, x: float, y: float,
action: int, pointerId: int = 0,
pressure: float = 1.0) -> Optional[GestureEvent]:
if not self._enabled:
return None
if action == 0:
self._start_x = x
self._start_y = y
self._is_dragging = False
elif action == 2:
dx = x - self._start_x
dy = y - self._start_y
dist = math.sqrt(dx * dx + dy * dy)
if dist > 15.0:
self._is_dragging = True
return GestureEvent(
eventType=GESTURE_EVENT_TYPE_DRAG,
position=(x, y),
state=GESTURE_STATE_IN_PROGRESS,
)
elif action == 1 and self._is_dragging:
self._is_dragging = False
return None
class MultiFingerGesture(GestureRecognizer):
"""MultiFingerGesture — 多指手势识别器"""
def __init__(self, minFingers: int = 2):
super().__init__()
self._min_fingers = minFingers
self._active_pointers: Dict[int, Tuple[float, float]] = {}
def processTouchEvent(self, x: float, y: float,
action: int, pointerId: int = 0,
pressure: float = 1.0) -> Optional[GestureEvent]:
if not self._enabled:
return None
if action == 0:
self._active_pointers[pointerId] = (x, y)
elif action == 1:
self._active_pointers.pop(pointerId, None)
if len(self._active_pointers) >= self._min_fingers:
return GestureEvent(
eventType=GESTURE_EVENT_TYPE_MULTI_FINGER_TAP,
position=(x, y),
fingerCount=len(self._active_pointers),
state=GESTURE_STATE_COMPLETED,
)
elif action == 2:
self._active_pointers[pointerId] = (x, y)
return None
# ═══════════════════════════════════════════════════════════════
# OrientationGesture
# ═══════════════════════════════════════════════════════════════
class OrientationGesture(GestureRecognizer):
"""OrientationGesture — 方向手势识别器"""
def __init__(self, orientation: int = ORIENTATION_ANY):
super().__init__()
self._orientation = orientation
self._start_x = 0.0
self._start_y = 0.0
self._is_tracking = False
def processTouchEvent(self, x: float, y: float,
action: int, pointerId: int = 0,
pressure: float = 1.0) -> Optional[GestureEvent]:
if not self._enabled:
return None
if action == 0:
self._start_x = x
self._start_y = y
self._is_tracking = True
elif action == 1 and self._is_tracking:
dx = x - self._start_x
dy = y - self._start_y
orientation = self._compute_orientation(dx, dy)
if self._orientation == ORIENTATION_ANY or self._orientation == orientation:
self._is_tracking = False
return GestureEvent(
eventType=GESTURE_EVENT_TYPE_ORIENTATION,
position=(x, y),
state=GESTURE_STATE_COMPLETED,
)
self._is_tracking = False
return None
def _compute_orientation(self, dx: float, dy: float) -> int:
adx = abs(dx)
ady = abs(dy)
if adx < 10 and ady < 10:
return ORIENTATION_ANY
if adx > ady * 2:
return ORIENTATION_HORIZONTAL if dx > 0 else ORIENTATION_LEFT
elif ady > adx * 2:
return ORIENTATION_VERTICAL if dy > 0 else ORIENTATION_UP
elif dx > 0 and dy > 0:
return ORIENTATION_DIAGONAL_DOWN
elif dx > 0 and dy < 0:
return ORIENTATION_DIAGONAL_UP
elif dx < 0 and dy > 0:
return ORIENTATION_DIAGONAL_DOWN
return ORIENTATION_DIAGONAL_UP
# ═══════════════════════════════════════════════════════════════
# GestureOverlayView (扩展)
# ═══════════════════════════════════════════════════════════════
class GestureOverlayView(Stub):
"""GestureOverlayView — 手势叠加视图 (完整版)"""
GESTURE_STROKE_TYPE_SINGLE = 0
GESTURE_STROKE_TYPE_MULTIPLE = 1
ORIENTATION_HORIZONTAL = ORIENTATION_HORIZONTAL
ORIENTATION_VERTICAL = ORIENTATION_VERTICAL
def __init__(self, context: Any = None):
super().__init__()
self._context = context
self._current_stroke: Optional[GestureStroke] = None
self._gestures: List[GestureStroke] = []
self._stroke_type = GestureOverlayView.GESTURE_STROKE_TYPE_SINGLE
self._gesture_color = 0xFFFFFF00
self._gesture_length = 0.0
self._is_gesture_visible = True
self._events_enabled = True
self._performed_listeners: List[GesturePerformedListener] = []
self._drawing_listeners: List[GestureDrawingListener] = []
self._gesture_recognizer = GestureRecognizer()
def getCurrentStroke(self) -> Optional[GestureStroke]:
return self._current_stroke
def getGestures(self) -> List[GestureStroke]:
return list(self._gestures)
def setGestureColor(self, color: int):
self._gesture_color = color
def getGestureColor(self) -> int:
return self._gesture_color
def setGestureStrokeType(self, type_: int):
self._stroke_type = type_
def getGestureStrokeType(self) -> int:
return self._stroke_type
def setGestureStrokeLengthThreshold(self, length: float):
self._gesture_length = length
def getGestureStrokeLengthThreshold(self) -> float:
return self._gesture_length
def setGestureVisible(self, visible: bool):
self._is_gesture_visible = visible
def isGestureVisible(self) -> bool:
return self._is_gesture_visible
def setEventsEnabled(self, enabled: bool):
self._events_enabled = enabled
def isEventsEnabled(self) -> bool:
return self._events_enabled
def addOnGesturePerformedListener(self, listener: GesturePerformedListener):
self._performed_listeners.append(listener)
def removeOnGesturePerformedListener(self, listener: GesturePerformedListener):
self._performed_listeners = [l for l in self._performed_listeners if l is not listener]
def addOnGestureDrawingListener(self, listener: GestureDrawingListener):
self._drawing_listeners.append(listener)
def removeOnGestureDrawingListener(self, listener: GestureDrawingListener):
self._drawing_listeners = [l for l in self._drawing_listeners if l is not listener]
def clear(self):
self._gestures.clear()
self._current_stroke = None
def cancelGesture(self):
if self._current_stroke:
for l in self._drawing_listeners:
l.onGestureCancelled(self._current_stroke)
self._current_stroke = None
def toString(self) -> str:
return f"GestureOverlayView(strokes={len(self._gestures)})"
def equals(self, other: Any) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# GestureBuilderActivity
# ═══════════════════════════════════════════════════════════════
class GestureBuilderActivity(Stub):
"""GestureBuilderActivity — 手势构建器活动"""
def __init__(self):
super().__init__()
self._overlay = GestureOverlayView()
self._library: Optional["GestureLibraries"] = None
self._current_name: str = ""
def getGestureOverlayView(self) -> GestureOverlayView:
return self._overlay
def setGestureLibrary(self, library: "GestureLibraries"):
self._library = library
def getGestureLibrary(self) -> Optional["GestureLibraries"]:
return self._library
def setCurrentGestureName(self, name: str):
self._current_name = name
def getCurrentGestureName(self) -> str:
return self._current_name
def saveGesture(self):
if self._library and self._current_name and self._overlay.getCurrentStroke():
stroke = self._overlay.getCurrentStroke()
self._library.store(self._current_name, stroke)
self._library.save()
def toString(self) -> str:
return f"GestureBuilderActivity(name={self._current_name})"
def equals(self, other: Any) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# GestureLibraries
# ═══════════════════════════════════════════════════════════════
class GestureLibraries(Stub):
"""GestureLibraries — 手势库"""
def __init__(self, storeType: int = GESTURE_LIBRARY_FILE,
path: str = "", loadOnInit: bool = True):
super().__init__()
self._store_type = storeType
self._path = path
self._gestures: Dict[str, List[GestureStroke]] = {}
self._loaded = False if loadOnInit else False
def load(self) -> bool:
self._loaded = True
return True
def save(self) -> bool:
return True
def isLoaded(self) -> bool:
return self._loaded
def getGestures(self, name: str) -> List[GestureStroke]:
return self._gestures.get(name, [])
def getGestureNames(self) -> List[str]:
return list(self._gestures.keys())
def store(self, name: str, stroke: GestureStroke):
if name not in self._gestures:
self._gestures[name] = []
self._gestures[name].append(stroke)
def remove(self, name: str):
self._gestures.pop(name, None)
def removeEntry(self, name: str, stroke: GestureStroke):
strokes = self._gestures.get(name)
if strokes:
try:
strokes.remove(stroke)
except ValueError:
pass
if not strokes:
self._gestures.pop(name, None)
def getCount(self) -> int:
return sum(len(s) for s in self._gestures.values())
@staticmethod
def fromFile(path: str) -> "GestureLibraries":
return GestureLibraries(storeType=GESTURE_LIBRARY_FILE, path=path)
@staticmethod
def fromResource(context: Any, resourceId: int) -> "GestureLibraries":
return GestureLibraries(storeType=GESTURE_LIBRARY_RESOURCE, path=str(resourceId))
@staticmethod
def fromRaw(context: Any, resourceId: int) -> "GestureLibraries":
return GestureLibraries(storeType=GESTURE_LIBRARY_RAW, path=str(resourceId))
@staticmethod
def fromPrivateFile(context: Any, name: str) -> "GestureLibraries":
return GestureLibraries(storeType=GESTURE_LIBRARY_PRIVATE, path=name)
def toString(self) -> str:
return f"GestureLibraries(type={self._store_type}, gestures={self.getCount()})"
def equals(self, other: Any) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF