""" Android View Animator 组件 — LayoutAnimationController, AnimationUtils, AnimationSet, Interpolator 系列, RotateAnimation, ScaleAnimation, TranslateAnimation, AlphaAnimation, GridLayoutAnimationController 等 方法签名完全对齐 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__ = [ "LayoutAnimationController", "AnimationUtils", "AnimationSet", "AnimationScale", "AnimationInterpolator", "CycleInterpolator", "AccelerateInterpolator", "DecelerateInterpolator", "AccelerateDecelerateInterpolator", "AnticipateInterpolator", "OvershootInterpolator", "AnticipateOvershootInterpolator", "BounceInterpolator", "LinearInterpolator", "PathInterpolator", "RotateAnimation", "ScaleAnimation", "TranslateAnimation", "AlphaAnimation", "AnimationDescription", "GridLayoutAnimationController", ] # ═══════════════════════════════════════════════════════════════ # 常量 # ═══════════════════════════════════════════════════════════════ INFINITE = -1 RESTART = 1 REVERSE = 2 ZORDER_NORMAL = 0 ZORDER_TOP = 1 ZORDER_BOTTOM = -1 RELATIVE_TO_SELF = 0 RELATIVE_TO_PARENT = 1 FILL_AFTER = 1 FILL_BEFORE = 2 FILL_BOTH = 3 START_ON_FIRST_FRAME = 4 ABSOLUTE_TO_SELF = 8 ANIMATION_STARTED = 0 ANIMATION_RUNNING = 1 ANIMATION_FILLED = 2 ANIMATION_ENDED = 3 ANIMATION_CANCELLED = 4 # ═══════════════════════════════════════════════════════════════ # AnimationInterpolator # ═══════════════════════════════════════════════════════════════ class AnimationInterpolator(Stub): """AnimationInterpolator — 动画插值器基类""" def getInterpolation(self, input: float) -> float: return input def toString(self) -> str: return f"{self.__class__.__name__}@{hex(id(self))}" def equals(self, other: Any) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF class LinearInterpolator(AnimationInterpolator): """LinearInterpolator — 线性插值器""" def getInterpolation(self, input: float) -> float: return input def toString(self) -> str: return "LinearInterpolator" class AccelerateInterpolator(AnimationInterpolator): """AccelerateInterpolator — 加速插值器""" def __init__(self, factor: float = 1.0): super().__init__() self._factor = factor self._double_factor = 2.0 * factor def getInterpolation(self, input: float) -> float: if self._factor == 1.0: return input * input return pow(input, self._double_factor) def toString(self) -> str: return f"AccelerateInterpolator(factor={self._factor})" class DecelerateInterpolator(AnimationInterpolator): """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 - pow(1.0 - input, 2.0 * self._factor) def toString(self) -> str: return f"DecelerateInterpolator(factor={self._factor})" class AccelerateDecelerateInterpolator(AnimationInterpolator): """AccelerateDecelerateInterpolator — 加速减速插值器""" def getInterpolation(self, input: float) -> float: return float(math.cos((input + 1.0) * math.pi) / 2.0 + 0.5) def toString(self) -> str: return "AccelerateDecelerateInterpolator" class AnticipateInterpolator(AnimationInterpolator): """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.0) * input - self._tension) def toString(self) -> str: return f"AnticipateInterpolator(tension={self._tension})" class OvershootInterpolator(AnimationInterpolator): """OvershootInterpolator — 超调插值器""" def __init__(self, tension: float = 2.0): super().__init__() self._tension = tension def getInterpolation(self, input: float) -> float: x = input - 1.0 return x * x * ((self._tension + 1.0) * x + self._tension) + 1.0 def toString(self) -> str: return f"OvershootInterpolator(tension={self._tension})" class AnticipateOvershootInterpolator(AnimationInterpolator): """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: x = 2.0 * input return 0.5 * x * x * ((self._tension + 1.0) * x - self._tension) else: x = 2.0 * input - 2.0 t = self._tension * self._extra_tension return 0.5 * (x * x * ((t + 1.0) * x + t) + 2.0) def toString(self) -> str: return f"AnticipateOvershootInterpolator(tension={self._tension})" class BounceInterpolator(AnimationInterpolator): """BounceInterpolator — 弹跳插值器""" def getInterpolation(self, input: float) -> float: x = input for i in range(4): duration = pow(2.0, -i) * 0.6 if x < duration: scale = 0.5 / duration return scale * x * (duration - x) * 8.0 + (1.0 - 1.0 / pow(2.0, i)) x -= duration return 0.0 def toString(self) -> str: return "BounceInterpolator" class CycleInterpolator(AnimationInterpolator): """CycleInterpolator — 周期插值器""" def __init__(self, cycles: float = 1.0): super().__init__() self._cycles = cycles def getInterpolation(self, input: float) -> float: return float(math.sin(2.0 * math.pi * self._cycles * input)) def toString(self) -> str: return f"CycleInterpolator(cycles={self._cycles})" class PathInterpolator(AnimationInterpolator): """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: t = input return (1.0 - t) * (1.0 - t) * (1.0 - t) * 0.0 + \ 3.0 * (1.0 - t) * (1.0 - t) * t * self._cy1 + \ 3.0 * (1.0 - t) * t * t * self._cy2 + \ t * t * t * 1.0 def toString(self) -> str: return f"PathInterpolator({self._cx1}, {self._cy1}, {self._cx2}, {self._cy2})" # ═══════════════════════════════════════════════════════════════ # AnimationScale # ═══════════════════════════════════════════════════════════════ class AnimationScale(Stub): """AnimationScale — 动画缩放比例""" def __init__(self, scale: float = 1.0): super().__init__() self._scale = scale def getScale(self) -> float: return self._scale def setScale(self, scale: float): self._scale = max(0.0, scale) def toString(self) -> str: return f"AnimationScale(scale={self._scale})" # ═══════════════════════════════════════════════════════════════ # AnimationSet # ═══════════════════════════════════════════════════════════════ class AnimationSet(Stub): """AnimationSet — 动画集合""" def __init__(self, shareInterpolator: bool = True): super().__init__() self._share_interpolator = shareInterpolator self._animations: List[Any] = [] self._interpolator: Optional[AnimationInterpolator] = None self._duration: int = 0 self._fill_after = False self._fill_before = False self._repeat_count = 0 self._repeat_mode = RESTART self._start_offset = 0 self._start_time = -1 def addAnimation(self, animation: Any): self._animations.append(animation) def getAnimations(self) -> List[Any]: return list(self._animations) def setDuration(self, duration: int): self._duration = duration for anim in self._animations: if hasattr(anim, 'setDuration'): anim.setDuration(duration) def getDuration(self) -> int: return self._duration def setInterpolator(self, interpolator: AnimationInterpolator): self._interpolator = interpolator if self._share_interpolator: for anim in self._animations: if hasattr(anim, 'setInterpolator'): anim.setInterpolator(interpolator) def getInterpolator(self) -> Optional[AnimationInterpolator]: return self._interpolator def setFillAfter(self, fill: bool): self._fill_after = fill def isFillAfter(self) -> bool: return self._fill_after def setFillBefore(self, fill: bool): self._fill_before = fill def isFillBefore(self) -> bool: return self._fill_before 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 setStartOffset(self, offset: int): self._start_offset = offset def getStartOffset(self) -> int: return self._start_offset def start(self): self._start_time = int(time.time() * 1000) def cancel(self): self._start_time = -1 def isRunning(self) -> bool: return self._start_time >= 0 def hasStarted(self) -> bool: return self._start_time >= 0 def toString(self) -> str: return f"AnimationSet(animations={len(self._animations)}, duration={self._duration})" def equals(self, other: Any) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # AnimationDescription # ═══════════════════════════════════════════════════════════════ class AnimationDescription(Stub): """AnimationDescription — 动画描述""" def __init__(self, name: str = "", duration: int = 300, interpolator: Optional[AnimationInterpolator] = None, fillAfter: bool = False, repeatCount: int = 0): super().__init__() self._name = name self._duration = duration self._interpolator = interpolator or LinearInterpolator() self._fill_after = fillAfter self._repeat_count = repeatCount def getName(self) -> str: return self._name def getDuration(self) -> int: return self._duration def getInterpolator(self) -> AnimationInterpolator: return self._interpolator def isFillAfter(self) -> bool: return self._fill_after def getRepeatCount(self) -> int: return self._repeat_count def toString(self) -> str: return f"AnimationDescription(name={self._name}, duration={self._duration})" # ═══════════════════════════════════════════════════════════════ # RotateAnimation # ═══════════════════════════════════════════════════════════════ class RotateAnimation(Stub): """RotateAnimation — 旋转动画""" def __init__(self, fromDegrees: float = 0.0, toDegrees: float = 360.0, pivotX: float = 0.0, pivotY: float = 0.0, pivotXType: int = RELATIVE_TO_SELF, pivotYType: int = RELATIVE_TO_SELF): super().__init__() self._from_degrees = fromDegrees self._to_degrees = toDegrees self._pivot_x = pivotX self._pivot_y = pivotY self._pivot_x_type = pivotXType self._pivot_y_type = pivotYType self._duration = 300 self._interpolator: Optional[AnimationInterpolator] = None self._repeat_count = 0 self._repeat_mode = RESTART self._fill_after = False self._fill_before = False self._start_offset = 0 self._start_time = -1 def getFromDegrees(self) -> float: return self._from_degrees def getToDegrees(self) -> float: return self._to_degrees def getPivotX(self) -> float: return self._pivot_x def getPivotY(self) -> float: return self._pivot_y def getPivotXType(self) -> int: return self._pivot_x_type def getPivotYType(self) -> int: return self._pivot_y_type def setDuration(self, duration: int): self._duration = duration def getDuration(self) -> int: return self._duration def setInterpolator(self, interpolator: AnimationInterpolator): self._interpolator = interpolator def getInterpolator(self) -> Optional[AnimationInterpolator]: return self._interpolator 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 setFillAfter(self, fill: bool): self._fill_after = fill def isFillAfter(self) -> bool: return self._fill_after def setFillBefore(self, fill: bool): self._fill_before = fill def isFillBefore(self) -> bool: return self._fill_before def setStartOffset(self, offset: int): self._start_offset = offset def start(self): self._start_time = int(time.time() * 1000) def cancel(self): self._start_time = -1 def toString(self) -> str: return (f"RotateAnimation(from={self._from_degrees}, to={self._to_degrees}, " f"duration={self._duration})") def equals(self, other: Any) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # ScaleAnimation # ═══════════════════════════════════════════════════════════════ class ScaleAnimation(Stub): """ScaleAnimation — 缩放动画""" def __init__(self, fromX: float = 0.0, toX: float = 1.0, fromY: float = 0.0, toY: float = 1.0, pivotX: float = 0.0, pivotY: float = 0.0, pivotXType: int = RELATIVE_TO_SELF, pivotYType: int = RELATIVE_TO_SELF): super().__init__() self._from_x = fromX self._to_x = toX self._from_y = fromY self._to_y = toY self._pivot_x = pivotX self._pivot_y = pivotY self._pivot_x_type = pivotXType self._pivot_y_type = pivotYType self._duration = 300 self._interpolator: Optional[AnimationInterpolator] = None self._repeat_count = 0 self._fill_after = False self._start_time = -1 def getFromX(self) -> float: return self._from_x def getToX(self) -> float: return self._to_x def getFromY(self) -> float: return self._from_y def getToY(self) -> float: return self._to_y def getPivotX(self) -> float: return self._pivot_x def getPivotY(self) -> float: return self._pivot_y def setDuration(self, duration: int): self._duration = duration def getDuration(self) -> int: return self._duration def setInterpolator(self, interpolator: AnimationInterpolator): self._interpolator = interpolator def getInterpolator(self) -> Optional[AnimationInterpolator]: return self._interpolator def setRepeatCount(self, count: int): self._repeat_count = count def setFillAfter(self, fill: bool): self._fill_after = fill def start(self): self._start_time = int(time.time() * 1000) def cancel(self): self._start_time = -1 def toString(self) -> str: return (f"ScaleAnimation(x={self._from_x}->{self._to_x}, " f"y={self._from_y}->{self._to_y})") # ═══════════════════════════════════════════════════════════════ # TranslateAnimation (扩展) # ═══════════════════════════════════════════════════════════════ class TranslateAnimation(Stub): """TranslateAnimation — 位移动画 (完整版)""" def __init__(self, fromX: float = 0.0, toX: float = 0.0, fromY: float = 0.0, toY: float = 0.0, fromXType: int = RELATIVE_TO_SELF, toXType: int = RELATIVE_TO_SELF, fromYType: int = RELATIVE_TO_SELF, toYType: int = RELATIVE_TO_SELF): super().__init__() self._from_x = fromX self._to_x = toX self._from_y = fromY self._to_y = toY self._from_x_type = fromXType self._to_x_type = toXType self._from_y_type = fromYType self._to_y_type = toYType self._duration = 300 self._interpolator: Optional[AnimationInterpolator] = None self._repeat_count = 0 self._fill_after = False self._start_time = -1 def getFromX(self) -> float: return self._from_x def getToX(self) -> float: return self._to_x def getFromY(self) -> float: return self._from_y def getToY(self) -> float: return self._to_y def setDuration(self, duration: int): self._duration = duration def getDuration(self) -> int: return self._duration def setInterpolator(self, interpolator: AnimationInterpolator): self._interpolator = interpolator def setRepeatCount(self, count: int): self._repeat_count = count def setFillAfter(self, fill: bool): self._fill_after = fill def start(self): self._start_time = int(time.time() * 1000) def cancel(self): self._start_time = -1 def hasStarted(self) -> bool: return self._start_time >= 0 def toString(self) -> str: return (f"TranslateAnimation(x={self._from_x}->{self._to_x}, " f"y={self._from_y}->{self._to_y})") # ═══════════════════════════════════════════════════════════════ # AlphaAnimation (扩展) # ═══════════════════════════════════════════════════════════════ class AlphaAnimation(Stub): """AlphaAnimation — 透明度动画 (完整版)""" def __init__(self, fromAlpha: float = 0.0, toAlpha: float = 1.0): super().__init__() self._from_alpha = fromAlpha self._to_alpha = toAlpha self._duration = 300 self._interpolator: Optional[AnimationInterpolator] = None self._repeat_count = 0 self._fill_after = False self._start_time = -1 def getFromAlpha(self) -> float: return self._from_alpha def getToAlpha(self) -> float: return self._to_alpha def setDuration(self, duration: int): self._duration = duration def getDuration(self) -> int: return self._duration def setInterpolator(self, interpolator: AnimationInterpolator): self._interpolator = interpolator def getInterpolator(self) -> Optional[AnimationInterpolator]: return self._interpolator def setRepeatCount(self, count: int): self._repeat_count = count def setFillAfter(self, fill: bool): self._fill_after = fill def start(self): self._start_time = int(time.time() * 1000) def cancel(self): self._start_time = -1 def hasStarted(self) -> bool: return self._start_time >= 0 def toString(self) -> str: return f"AlphaAnimation(from={self._from_alpha}, to={self._to_alpha})" # ═══════════════════════════════════════════════════════════════ # LayoutAnimationController # ═══════════════════════════════════════════════════════════════ class LayoutAnimationController(Stub): """LayoutAnimationController — 布局动画控制器""" ORDER_NORMAL = 0 ORDER_REVERSE = 1 ORDER_RANDOM = 2 def __init__(self, animation: Optional[AnimationDescription] = None, delay: float = 0.5, order: int = ORDER_NORMAL): super().__init__() self._animation = animation self._delay = delay self._order = order self._interpolator: Optional[AnimationInterpolator] = None def getAnimation(self) -> Optional[AnimationDescription]: return self._animation def setAnimation(self, animation: AnimationDescription): self._animation = animation def getDelay(self) -> float: return self._delay def setDelay(self, delay: float): self._delay = delay def getOrder(self) -> int: return self._order def setOrder(self, order: int): self._order = order def setInterpolator(self, interpolator: AnimationInterpolator): self._interpolator = interpolator def getInterpolator(self) -> Optional[AnimationInterpolator]: return self._interpolator def isEnabled(self) -> bool: return self._animation is not None def toString(self) -> str: return f"LayoutAnimationController(delay={self._delay})" def equals(self, other: Any) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # GridLayoutAnimationController # ═══════════════════════════════════════════════════════════════ class GridLayoutAnimationController(LayoutAnimationController): """GridLayoutAnimationController — 网格布局动画控制器""" DIRECTION_LEFT_TO_RIGHT = 0 DIRECTION_RIGHT_TO_LEFT = 1 DIRECTION_TOP_TO_BOTTOM = 2 DIRECTION_BOTTOM_TO_TOP = 3 DIRECTION_HORIZONTAL_MASK = 1 DIRECTION_VERTICAL_MASK = 2 PRIORITY_NONE = 0 PRIORITY_COLUMN = 1 PRIORITY_ROW = 2 def __init__(self, animation: Optional[AnimationDescription] = None, columnDelay: float = 0.5, rowDelay: float = 0.5, direction: int = DIRECTION_LEFT_TO_RIGHT, directionPriority: int = PRIORITY_NONE): super().__init__(animation=animation, delay=0.5) self._column_delay = columnDelay self._row_delay = rowDelay self._direction = direction self._direction_priority = directionPriority def getColumnDelay(self) -> float: return self._column_delay def setColumnDelay(self, delay: float): self._column_delay = delay def getRowDelay(self) -> float: return self._row_delay def setRowDelay(self, delay: float): self._row_delay = delay def getDirection(self) -> int: return self._direction def setDirection(self, direction: int): self._direction = direction def getDirectionPriority(self) -> int: return self._direction_priority def setDirectionPriority(self, priority: int): self._direction_priority = priority def toString(self) -> str: return f"GridLayoutAnimationController(colDelay={self._column_delay})" # ═══════════════════════════════════════════════════════════════ # AnimationUtils # ═══════════════════════════════════════════════════════════════ class AnimationUtils(Stub): """AnimationUtils — 动画工具类""" @staticmethod def currentAnimationTimeMillis() -> int: return int(time.time() * 1000) @staticmethod def loadAnimation(context: Any, id: int) -> Optional[AnimationDescription]: return None @staticmethod def loadInterpolator(context: Any, id: int) -> Optional[AnimationInterpolator]: return None @staticmethod def makeInAnimation(context: Any, fromLeft: bool = True) -> TranslateAnimation: x = -1.0 if fromLeft else 1.0 anim = TranslateAnimation(fromX=x, toX=0.0, fromXType=RELATIVE_TO_PARENT, toXType=RELATIVE_TO_PARENT) anim.setDuration(300) anim.setInterpolator(DecelerateInterpolator()) return anim @staticmethod def makeOutAnimation(context: Any, toRight: bool = True) -> TranslateAnimation: x = 1.0 if toRight else -1.0 anim = TranslateAnimation(fromX=0.0, toX=x, fromXType=RELATIVE_TO_PARENT, toXType=RELATIVE_TO_PARENT) anim.setDuration(300) anim.setInterpolator(AccelerateInterpolator()) return anim @staticmethod def makeInChildBottomAnimation(context: Any) -> TranslateAnimation: anim = TranslateAnimation(fromX=0.0, toX=0.0, fromY=1.0, toY=0.0, fromYType=RELATIVE_TO_PARENT, toYType=RELATIVE_TO_PARENT) anim.setDuration(300) anim.setInterpolator(DecelerateInterpolator()) return anim @staticmethod def createScaleUpAnimation(context: Any) -> ScaleAnimation: anim = ScaleAnimation(fromX=0.0, toX=1.0, fromY=0.0, toY=1.0, pivotX=0.5, pivotY=0.5, pivotXType=RELATIVE_TO_SELF, pivotYType=RELATIVE_TO_SELF) anim.setDuration(300) anim.setInterpolator(AccelerateDecelerateInterpolator()) return anim @staticmethod def createShrinkDownAnimation(context: Any) -> ScaleAnimation: anim = ScaleAnimation(fromX=1.0, toX=0.0, fromY=1.0, toY=0.0, pivotX=0.5, pivotY=0.5, pivotXType=RELATIVE_TO_SELF, pivotYType=RELATIVE_TO_SELF) anim.setDuration(300) anim.setInterpolator(AccelerateDecelerateInterpolator()) return anim @staticmethod def createFadeInAnimation(context: Any) -> AlphaAnimation: anim = AlphaAnimation(fromAlpha=0.0, toAlpha=1.0) anim.setDuration(300) return anim @staticmethod def createFadeOutAnimation(context: Any) -> AlphaAnimation: anim = AlphaAnimation(fromAlpha=1.0, toAlpha=0.0) anim.setDuration(300) return anim @staticmethod def createRotateAnimation(context: Any, fromDeg: float = 0.0, toDeg: float = 360.0) -> RotateAnimation: anim = RotateAnimation(fromDegrees=fromDeg, toDegrees=toDeg, pivotX=0.5, pivotY=0.5) anim.setDuration(500) return anim def toString(self) -> str: return "AnimationUtils"