""" Android animation 组件 — Animator, ValueAnimator, ObjectAnimator, 插值器等 方法签名完全对齐 Android SDK """ from __future__ import annotations import math import threading import time as _time from typing import Any, Callable, Dict, List, Optional, Set, Tuple, TypeVar from .._core import Stub, AndroidOnlyError from ..java.lang import RuntimeException IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) # ═══════════════════════════════════════════════════════════════ # TypeEvaluator # ═══════════════════════════════════════════════════════════════ class TypeEvaluator(Stub): """TypeEvaluator — 类型估值器接口""" def evaluate(self, fraction: float, startValue: Any, endValue: Any) -> Any: return startValue class FloatEvaluator(TypeEvaluator): """FloatEvaluator — 浮点估值器""" def evaluate(self, fraction: float, startValue: Any, endValue: Any) -> float: sv = float(startValue) ev = float(endValue) return sv + fraction * (ev - sv) class IntEvaluator(TypeEvaluator): """IntEvaluator — 整数估值器""" def evaluate(self, fraction: float, startValue: Any, endValue: Any) -> int: sv = int(startValue) ev = int(endValue) return int(sv + fraction * (ev - sv)) class PointFEvaluator(TypeEvaluator): """PointFEvaluator — PointF 估值器""" def evaluate(self, fraction: float, startValue: Any, endValue: Any) -> Any: if hasattr(startValue, 'x') and hasattr(startValue, 'y'): sv_x, sv_y = startValue.x, startValue.y ev_x, ev_y = endValue.x, endValue.y class PointF: def __init__(self, x, y): self.x = x self.y = y return PointF(sv_x + fraction * (ev_x - sv_x), sv_y + fraction * (ev_y - sv_y)) return startValue class RectEvaluator(TypeEvaluator): """RectEvaluator — Rect 估值器""" def evaluate(self, fraction: float, startValue: Any, endValue: Any) -> Any: if hasattr(startValue, 'left') and hasattr(startValue, 'top'): class Rect: def __init__(self, l, t, r, b): self.left = l self.top = t self.right = r self.bottom = b return Rect( int(startValue.left + fraction * (endValue.left - startValue.left)), int(startValue.top + fraction * (endValue.top - startValue.top)), int(startValue.right + fraction * (endValue.right - startValue.right)), int(startValue.bottom + fraction * (endValue.bottom - startValue.bottom)), ) return startValue class ArgbEvaluator(TypeEvaluator): """ArgbEvaluator — ARGB 颜色估值器""" def evaluate(self, fraction: float, startValue: Any, endValue: Any) -> int: sv = int(startValue) ev = int(endValue) a = ((sv >> 24) & 0xFF) + int(fraction * (((ev >> 24) & 0xFF) - ((sv >> 24) & 0xFF))) r = ((sv >> 16) & 0xFF) + int(fraction * (((ev >> 16) & 0xFF) - ((sv >> 16) & 0xFF))) g = ((sv >> 8) & 0xFF) + int(fraction * (((ev >> 8) & 0xFF) - ((sv >> 8) & 0xFF))) b = (sv & 0xFF) + int(fraction * ((ev & 0xFF) - (sv & 0xFF))) return (min(a, 255) << 24) | (min(r, 255) << 16) | (min(g, 255) << 8) | min(b, 255) # ═══════════════════════════════════════════════════════════════ # TimeInterpolator # ═══════════════════════════════════════════════════════════════ class TimeInterpolator(Stub): """TimeInterpolator — 时间插值器接口""" def getInterpolation(self, input_: float) -> float: return input_ class AccelerateDecelerateInterpolator(TimeInterpolator): """AccelerateDecelerateInterpolator — 加速减速插值器""" def getInterpolation(self, input_: float) -> float: return (math.cos((input_ + 1) * math.pi) / 2.0) + 0.5 class AccelerateInterpolator(TimeInterpolator): """AccelerateInterpolator — 加速插值器""" def __init__(self, factor: float = 1.0): super().__init__() self._factor = factor def getInterpolation(self, input_: float) -> float: if self._factor == 1.0: return input_ * input_ return input_ ** (2.0 * self._factor) class DecelerateInterpolator(TimeInterpolator): """DecelerateInterpolator — 减速插值器""" def __init__(self, factor: float = 1.0): super().__init__() self._factor = factor def getInterpolation(self, input_: float) -> float: if self._factor == 1.0: return 1.0 - (1.0 - input_) * (1.0 - input_) return 1.0 - (1.0 - input_) ** (2.0 * self._factor) class LinearInterpolator(TimeInterpolator): """LinearInterpolator — 线性插值器""" def getInterpolation(self, input_: float) -> float: return input_ class BounceInterpolator(TimeInterpolator): """BounceInterpolator — 弹跳插值器""" def getInterpolation(self, input_: float) -> float: t = input_ if t < 0.363636: return 7.5625 * t * t elif t < 0.727272: t -= 0.545454 return 7.5625 * t * t + 0.75 elif t < 0.909090: t -= 0.818181 return 7.5625 * t * t + 0.9375 else: t -= 0.954545 return 7.5625 * t * t + 0.984375 class AnticipateInterpolator(TimeInterpolator): """AnticipateInterpolator — 预期插值器""" def __init__(self, tension: float = 2.0): super().__init__() self._tension = tension def getInterpolation(self, input_: float) -> float: return input_ * input_ * ((self._tension + 1) * input_ - self._tension) class OvershootInterpolator(TimeInterpolator): """OvershootInterpolator — 过冲插值器""" def __init__(self, tension: float = 2.0): super().__init__() self._tension = tension def getInterpolation(self, input_: float) -> float: t = input_ - 1.0 return t * t * ((self._tension + 1) * t + self._tension) + 1.0 class AnticipateOvershootInterpolator(TimeInterpolator): """AnticipateOvershootInterpolator — 预期过冲插值器""" def __init__(self, tension: float = 2.0, extraTension: float = 1.5): super().__init__() self._tension = tension self._extra_tension = extraTension def getInterpolation(self, input_: float) -> float: if input_ < 0.5: t = input_ * 2.0 return 0.5 * t * t * ((self._tension + 1) * t - self._tension) else: t = (input_ - 0.5) * 2.0 - 1.0 return 0.5 * (t * t * ((self._tension + 1) * t + self._tension) + 2.0) class CycleInterpolator(TimeInterpolator): """CycleInterpolator — 周期插值器""" def __init__(self, cycles: float = 1.0): super().__init__() self._cycles = cycles def getInterpolation(self, input_: float) -> float: return math.sin(2 * math.pi * self._cycles * input_) class PathInterpolator(TimeInterpolator): """PathInterpolator — 路径插值器""" def __init__(self, controlX1: float = 0.0, controlY1: float = 0.0, controlX2: float = 1.0, controlY2: float = 1.0): super().__init__() self._cx1 = controlX1 self._cy1 = controlY1 self._cx2 = controlX2 self._cy2 = controlY2 def getInterpolation(self, input_: float) -> float: # Cubic bezier approximation t = input_ return (3 * self._cy1 * t * (1 - t) ** 2 + 3 * self._cy2 * t ** 2 * (1 - t) + t ** 3) # ═══════════════════════════════════════════════════════════════ # AnimatorListener # ═══════════════════════════════════════════════════════════════ class AnimatorListener(Stub): """AnimatorListener — 动画监听器""" def onAnimationStart(self, animation: "Animator"): pass def onAnimationEnd(self, animation: "Animator"): pass def onAnimationCancel(self, animation: "Animator"): pass def onAnimationRepeat(self, animation: "Animator"): pass class AnimatorPauseListener(Stub): """AnimatorPauseListener — 动画暂停监听器""" def onAnimationPause(self, animation: "Animator"): pass def onAnimationResume(self, animation: "Animator"): pass # ═══════════════════════════════════════════════════════════════ # Animator # ═══════════════════════════════════════════════════════════════ class Animator(Stub): """Animator — 动画基类""" def __init__(self): super().__init__() self._duration: int = 300 self._start_delay: int = 0 self._interpolator: Optional[TimeInterpolator] = None self._listeners: List[AnimatorListener] = [] self._pause_listeners: List[AnimatorPauseListener] = [] self._running: bool = False self._started: bool = False self._paused: bool = False self._start_time: float = 0.0 def start(self): self._running = True self._started = True self._start_time = _time.time() + self._start_delay / 1000.0 self._notify_start() def end(self): if self._running: self._running = False self._notify_end() def cancel(self): if self._running: self._running = False self._started = False self._notify_cancel() def pause(self): if self._running and not self._paused: self._paused = True for l in self._pause_listeners: l.onAnimationPause(self) def resume(self): if self._paused: self._paused = False for l in self._pause_listeners: l.onAnimationResume(self) def isRunning(self) -> bool: return self._running def isStarted(self) -> bool: return self._started def isPaused(self) -> bool: return self._paused def addListener(self, listener: AnimatorListener): if listener not in self._listeners: self._listeners.append(listener) def removeListener(self, listener: AnimatorListener): if listener in self._listeners: self._listeners.remove(listener) def getListeners(self) -> List[AnimatorListener]: return list(self._listeners) def addPauseListener(self, listener: AnimatorPauseListener): if listener not in self._pause_listeners: self._pause_listeners.append(listener) def removePauseListener(self, listener: AnimatorPauseListener): if listener in self._pause_listeners: self._pause_listeners.remove(listener) def setDuration(self, duration: int) -> "Animator": self._duration = duration return self def getDuration(self) -> int: return self._duration def setStartDelay(self, startDelay: int) -> "Animator": self._start_delay = startDelay return self def getStartDelay(self) -> int: return self._start_delay def setInterpolator(self, interpolator: TimeInterpolator): self._interpolator = interpolator def getInterpolator(self) -> Optional[TimeInterpolator]: return self._interpolator def clone(self) -> "Animator": cloned = Animator() cloned._duration = self._duration cloned._start_delay = self._start_delay cloned._interpolator = self._interpolator return cloned def _notify_start(self): for l in self._listeners: l.onAnimationStart(self) def _notify_end(self): for l in self._listeners: l.onAnimationEnd(self) def _notify_cancel(self): for l in self._listeners: l.onAnimationCancel(self) def _notify_repeat(self): for l in self._listeners: l.onAnimationRepeat(self) def toString(self) -> str: return f"Animator@{hex(id(self))}" # ═══════════════════════════════════════════════════════════════ # ValueAnimator.AnimatorUpdateListener # ═══════════════════════════════════════════════════════════════ class ValueAnimator: class AnimatorUpdateListener(Stub): """AnimatorUpdateListener — 值更新监听器""" def onAnimationUpdate(self, animation: "ValueAnimator"): pass # ═══════════════════════════════════════════════════════════════ # ValueAnimator # ═══════════════════════════════════════════════════════════════ class ValueAnimator(Animator): """ValueAnimator — 值动画""" INFINITE = -1 RESTART = 1 REVERSE = 2 def __init__(self): super().__init__() self._values: List[Any] = [] self._animated_value: Any = None self._animated_fraction: float = 0.0 self._repeat_count: int = 0 self._repeat_mode: int = self.RESTART self._evaluator: Optional[TypeEvaluator] = None self._update_listeners: List[ValueAnimator.AnimatorUpdateListener] = [] self._current_play_time: int = 0 self._reverse_direction: bool = False self._property_name: Optional[str] = None @staticmethod def ofInt(*values: int) -> "ValueAnimator": anim = ValueAnimator() anim._values = list(values) anim._evaluator = IntEvaluator() return anim @staticmethod def ofFloat(*values: float) -> "ValueAnimator": anim = ValueAnimator() anim._values = list(values) anim._evaluator = FloatEvaluator() return anim @staticmethod def ofObject(*values: Any) -> "ValueAnimator": anim = ValueAnimator() anim._values = list(values) return anim def setIntValues(self, *values: int): self._values = list(values) def setFloatValues(self, *values: float): self._values = list(values) def setObjectValues(self, *values: Any): self._values = list(values) def getAnimatedValue(self) -> Any: return self._animated_value def getAnimatedFraction(self) -> float: return self._animated_fraction def setRepeatCount(self, count: int): self._repeat_count = count def getRepeatCount(self) -> int: return self._repeat_count def setRepeatMode(self, mode: int): self._repeat_mode = mode def getRepeatMode(self) -> int: return self._repeat_mode def setEvaluator(self, evaluator: TypeEvaluator): self._evaluator = evaluator def getEvaluator(self) -> Optional[TypeEvaluator]: return self._evaluator def setCurrentPlayTime(self, playTime: int): self._current_play_time = playTime if self._duration > 0: self._animated_fraction = playTime / self._duration self._evaluate() def getCurrentPlayTime(self) -> int: return self._current_play_time def reverse(self): self._reverse_direction = not self._reverse_direction self.start() def addUpdateListener(self, listener: ValueAnimator.AnimatorUpdateListener): if listener not in self._update_listeners: self._update_listeners.append(listener) def removeUpdateListener(self, listener: ValueAnimator.AnimatorUpdateListener): if listener in self._update_listeners: self._update_listeners.remove(listener) def removeAllUpdateListeners(self): self._update_listeners.clear() def start(self): super().start() if len(self._values) >= 2: self._evaluate() def _evaluate(self): if len(self._values) >= 2 and self._evaluator: fraction = self._animated_fraction if self._interpolator: fraction = self._interpolator.getInterpolation(fraction) self._animated_value = self._evaluator.evaluate( fraction, self._values[0], self._values[-1] ) for l in self._update_listeners: l.onAnimationUpdate(self) def clone(self) -> "ValueAnimator": cloned = ValueAnimator() cloned._values = list(self._values) cloned._duration = self._duration cloned._start_delay = self._start_delay cloned._repeat_count = self._repeat_count cloned._repeat_mode = self._repeat_mode cloned._evaluator = self._evaluator cloned._interpolator = self._interpolator return cloned def toString(self) -> str: return f"ValueAnimator@{hex(id(self))}" # ═══════════════════════════════════════════════════════════════ # ObjectAnimator # ═══════════════════════════════════════════════════════════════ class ObjectAnimator(ValueAnimator): """ObjectAnimator — 对象属性动画""" def __init__(self): super().__init__() self._target: Any = None self._property_name: Optional[str] = None @staticmethod def ofInt(target: Any, propertyName: str, *values: int) -> "ObjectAnimator": anim = ObjectAnimator() anim._target = target anim._property_name = propertyName anim._values = list(values) anim._evaluator = IntEvaluator() return anim @staticmethod def ofFloat(target: Any, propertyName: str, *values: float) -> "ObjectAnimator": anim = ObjectAnimator() anim._target = target anim._property_name = propertyName anim._values = list(values) anim._evaluator = FloatEvaluator() return anim @staticmethod def ofObject(target: Any, propertyName: str, *values: Any) -> "ObjectAnimator": anim = ObjectAnimator() anim._target = target anim._property_name = propertyName anim._values = list(values) return anim def setPropertyName(self, propertyName: str): self._property_name = propertyName def getPropertyName(self) -> Optional[str]: return self._property_name def getTarget(self) -> Any: return self._target def setTarget(self, target: Any): self._target = target def setProperty(self, property_: Any): pass def setupStartValues(self): pass def setupEndValues(self): pass def clone(self) -> "ObjectAnimator": cloned = ObjectAnimator() cloned._target = self._target cloned._property_name = self._property_name cloned._values = list(self._values) cloned._duration = self._duration cloned._start_delay = self._start_delay cloned._evaluator = self._evaluator cloned._interpolator = self._interpolator return cloned def toString(self) -> str: return f"ObjectAnimator{{property={self._property_name}, target={self._target}}}" # ═══════════════════════════════════════════════════════════════ # AnimatorSet # ═══════════════════════════════════════════════════════════════ class AnimatorSet(Animator): """AnimatorSet — 动画集合""" def __init__(self): super().__init__() self._children: List[Animator] = [] self._play_order: List[Animator] = [] def play(self, anim: Animator) -> "AnimatorSet.Builder": return self.Builder(self, anim) def playSequentially(self, *items: Animator): self._children = list(items) self._play_order = list(items) def playTogether(self, *items: Animator): self._children = list(items) self._play_order = list(items) def start(self): super().start() for anim in self._play_order: anim.start() def end(self): super().end() for anim in self._play_order: anim.end() def cancel(self): super().cancel() for anim in self._play_order: anim.cancel() class Builder(Stub): """AnimatorSet.Builder — 构建器""" def __init__(self, parent: "AnimatorSet", anim: Animator): super().__init__() self._parent = parent self._current = anim def with_(self, anim: Animator) -> "AnimatorSet.Builder": self._parent._play_order.append(anim) return self def before(self, anim: Animator) -> "AnimatorSet.Builder": self._parent._play_order.append(anim) return self def after(self, anim: Animator) -> "AnimatorSet.Builder": idx = self._parent._play_order.index(self._current) if self._current in self._parent._play_order else -1 if idx >= 0: self._parent._play_order.insert(idx + 1, anim) else: self._parent._play_order.append(anim) return self def after(self, delay: int) -> "AnimatorSet.Builder": return self def toString(self) -> str: return f"AnimatorSet{{{len(self._children)} animations}}" # ═══════════════════════════════════════════════════════════════ # TimeAnimator # ═══════════════════════════════════════════════════════════════ class TimeAnimator(ValueAnimator): """TimeAnimator — 时间动画""" class TimeListener(Stub): """TimeListener — 时间监听器""" def onTimeUpdate(self, animation: "TimeAnimator", elapsedTime: int, totalTime: int): pass def __init__(self): super().__init__() self._time_listener: Optional[TimeAnimator.TimeListener] = None self._elapsed_time: int = 0 self._total_time: int = 0 def setTimeListener(self, listener: TimeAnimator.TimeListener): self._time_listener = listener def start(self): super().start() if self._time_listener: self._time_listener.onTimeUpdate(self, self._elapsed_time, self._total_time) def toString(self) -> str: return f"TimeAnimator@{hex(id(self))}" # ═══════════════════════════════════════════════════════════════ # Keyframe # ═══════════════════════════════════════════════════════════════ class Keyframe(Stub): """Keyframe — 关键帧""" def __init__(self, fraction: float): super().__init__() self._fraction: float = fraction self._value: Any = None self._type: Optional[type] = None self._interpolator: Optional[TimeInterpolator] = None @staticmethod def ofInt(fraction: float, value: int) -> "Keyframe": kf = Keyframe(fraction) kf._value = value kf._type = int return kf @staticmethod def ofFloat(fraction: float, value: float) -> "Keyframe": kf = Keyframe(fraction) kf._value = value kf._type = float return kf @staticmethod def ofObject(fraction: float, value: Any = None) -> "Keyframe": kf = Keyframe(fraction) kf._value = value return kf def setValue(self, value: Any): self._value = value def getValue(self) -> Any: return self._value def setFraction(self, fraction: float): self._fraction = fraction def getFraction(self) -> float: return self._fraction def getType(self) -> Optional[type]: return self._type def setInterpolator(self, interpolator: Optional[TimeInterpolator]): self._interpolator = interpolator def getInterpolator(self) -> Optional[TimeInterpolator]: return self._interpolator def clone(self) -> "Keyframe": kf = Keyframe(self._fraction) kf._value = self._value kf._type = self._type kf._interpolator = self._interpolator return kf def toString(self) -> str: return f"Keyframe{{fraction={self._fraction}, value={self._value}}}" # ═══════════════════════════════════════════════════════════════ # PropertyValuesHolder # ═══════════════════════════════════════════════════════════════ class PropertyValuesHolder(Stub): """PropertyValuesHolder — 属性值持有者""" def __init__(self): super().__init__() self._property_name: str = "" self._values: List[Any] = [] self._animated_value: Any = None self._evaluator: Optional[TypeEvaluator] = None self._keyframes: List[Keyframe] = [] @staticmethod def ofInt(propertyName: str, *values: int) -> "PropertyValuesHolder": holder = PropertyValuesHolder() holder._property_name = propertyName holder._values = list(values) return holder @staticmethod def ofFloat(propertyName: str, *values: float) -> "PropertyValuesHolder": holder = PropertyValuesHolder() holder._property_name = propertyName holder._values = list(values) return holder @staticmethod def ofObject(propertyName: str, *values: Any) -> "PropertyValuesHolder": holder = PropertyValuesHolder() holder._property_name = propertyName holder._values = list(values) return holder @staticmethod def ofKeyframes(propertyName: str, *keyframes: Keyframe) -> "PropertyValuesHolder": holder = PropertyValuesHolder() holder._property_name = propertyName holder._keyframes = list(keyframes) return holder def setPropertyName(self, propertyName: str): self._property_name = propertyName def getPropertyName(self) -> str: return self._property_name def getAnimatedValue(self) -> Any: return self._animated_value def setEvaluator(self, evaluator: TypeEvaluator): self._evaluator = evaluator def clone(self) -> "PropertyValuesHolder": holder = PropertyValuesHolder() holder._property_name = self._property_name holder._values = list(self._values) holder._keyframes = list(self._keyframes) holder._evaluator = self._evaluator return holder def toString(self) -> str: return f"PropertyValuesHolder{{property={self._property_name}}}" # ═══════════════════════════════════════════════════════════════ # TypeConverter # ═══════════════════════════════════════════════════════════════ class TypeConverter(Stub): """TypeConverter — 类型转换器""" def convert(self, value: Any) -> Any: return value # ═══════════════════════════════════════════════════════════════ # LayoutTransition # ═══════════════════════════════════════════════════════════════ class LayoutTransition(Stub): """LayoutTransition — 布局过渡动画""" APPEARING = 0 CHANGE_APPEARING = 1 CHANGE_DISAPPEARING = 2 DISAPPEARING = 3 CHANGING = 4 def __init__(self): super().__init__() self._animators: Dict[int, Optional[Animator]] = { self.APPEARING: None, self.CHANGE_APPEARING: None, self.CHANGE_DISAPPEARING: None, self.DISAPPEARING: None, self.CHANGING: None, } def setAnimator(self, transitionType: int, animator: Animator): self._animators[transitionType] = animator def getAnimator(self, transitionType: int) -> Optional[Animator]: return self._animators.get(transitionType) def startTransition(self, container: Any, transitionType: int): pass def endTransition(self, transitionType: int): pass def toString(self) -> str: return f"LayoutTransition@{hex(id(self))}" # ═══════════════════════════════════════════════════════════════ # StateListAnimator # ═══════════════════════════════════════════════════════════════ class StateListAnimator(Stub): """StateListAnimator — 状态列表动画""" def __init__(self): super().__init__() self._tuples: List[Tuple[List[int], Animator]] = [] self._target: Any = None def addState(self, specs: List[int], animator: Animator): self._tuples.append((specs, animator)) def setTarget(self, target: Any): self._target = target def clone(self) -> "StateListAnimator": cloned = StateListAnimator() cloned._tuples = list(self._tuples) return cloned def toString(self) -> str: return f"StateListAnimator{{{len(self._tuples)} states}}" # ═══════════════════════════════════════════════════════════════ # __all__ # ═══════════════════════════════════════════════════════════════ __all__ = [ "Animator", "ValueAnimator", "ObjectAnimator", "AnimatorSet", "TimeAnimator", "AnimatorListener", "AnimatorPauseListener", "TypeEvaluator", "FloatEvaluator", "IntEvaluator", "PointFEvaluator", "RectEvaluator", "ArgbEvaluator", "TypeConverter", "Keyframe", "PropertyValuesHolder", "LayoutTransition", "StateListAnimator", "TimeInterpolator", "AccelerateDecelerateInterpolator", "AccelerateInterpolator", "AnticipateInterpolator", "AnticipateOvershootInterpolator", "BounceInterpolator", "CycleInterpolator", "DecelerateInterpolator", "LinearInterpolator", "OvershootInterpolator", "PathInterpolator", ]