935 lines
34 KiB
Python
935 lines
34 KiB
Python
"""
|
|
Android View Animation 组件 — Animation, AnimationSet, Interpolators,
|
|
AnimationUtils, Transformation 等
|
|
方法签名完全对齐 Android SDK
|
|
"""
|
|
|
|
import math
|
|
import sys
|
|
import time
|
|
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
|
|
from .._core import Stub, AndroidOnlyError, android_context
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Interpolator
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Interpolator(Stub):
|
|
"""Interpolator — 插值器接口"""
|
|
|
|
def getInterpolation(self, input: float) -> float:
|
|
return input
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AccelerateInterpolator
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AccelerateInterpolator(Interpolator):
|
|
"""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 input ** (self._double_factor)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# DecelerateInterpolator
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class DecelerateInterpolator(Interpolator):
|
|
"""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)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AccelerateDecelerateInterpolator
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AccelerateDecelerateInterpolator(Interpolator):
|
|
"""AccelerateDecelerateInterpolator — 加速减速插值器"""
|
|
|
|
def getInterpolation(self, input: float) -> float:
|
|
return float(math.cos((input + 1) * math.pi) / 2.0 + 0.5)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# LinearInterpolator
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class LinearInterpolator(Interpolator):
|
|
"""LinearInterpolator — 线性插值器"""
|
|
|
|
def getInterpolation(self, input: float) -> float:
|
|
return input
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# OvershootInterpolator
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class OvershootInterpolator(Interpolator):
|
|
"""OvershootInterpolator — 超出插值器(越过目标再回弹)"""
|
|
|
|
def __init__(self, tension: float = 2.0):
|
|
super().__init__()
|
|
self._tension = tension
|
|
|
|
def getInterpolation(self, input: float) -> float:
|
|
input -= 1.0
|
|
return input * input * ((self._tension + 1) * input + self._tension) + 1.0
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AnticipateInterpolator
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AnticipateInterpolator(Interpolator):
|
|
"""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)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AnticipateOvershootInterpolator
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AnticipateOvershootInterpolator(Interpolator):
|
|
"""AnticipateOvershootInterpolator — 预判超出插值器"""
|
|
|
|
def __init__(self, tension: float = 2.0, extra_tension: float = 1.5):
|
|
super().__init__()
|
|
self._tension = tension
|
|
self._extra_tension = extra_tension
|
|
|
|
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 * 2.0 - 2.0
|
|
return 0.5 * (t * t * ((self._tension + 1) * t + self._tension) + 2.0)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# BounceInterpolator
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class BounceInterpolator(Interpolator):
|
|
"""BounceInterpolator — 弹跳插值器"""
|
|
|
|
@staticmethod
|
|
def _bounce(t: float) -> float:
|
|
t2 = t * t
|
|
return t2 * 8.0
|
|
|
|
def getInterpolation(self, input: float) -> float:
|
|
if input < 0.3535:
|
|
return BounceInterpolator._bounce(input)
|
|
elif input < 0.7408:
|
|
return BounceInterpolator._bounce(input - 0.54719) + 0.7
|
|
elif input < 0.9644:
|
|
return BounceInterpolator._bounce(input - 0.8526) + 0.9
|
|
else:
|
|
return BounceInterpolator._bounce(input - 1.0435) + 0.95
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# CycleInterpolator
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class CycleInterpolator(Interpolator):
|
|
"""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))
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# PathInterpolator
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class PathInterpolator(Interpolator):
|
|
"""PathInterpolator — 路径插值器"""
|
|
|
|
def __init__(self, control_x1: float = 0.0, control_y1: float = 0.0,
|
|
control_x2: float = 1.0, control_y2: float = 1.0):
|
|
super().__init__()
|
|
self._control_x1 = control_x1
|
|
self._control_y1 = control_y1
|
|
self._control_x2 = control_x2
|
|
self._control_y2 = control_y2
|
|
|
|
def getInterpolation(self, input: float) -> float:
|
|
return self._cubic_bezier(input)
|
|
|
|
def _cubic_bezier(self, t: float) -> float:
|
|
u = 1.0 - t
|
|
return (3.0 * u * u * t * self._control_y1 +
|
|
3.0 * u * t * t * self._control_y2 +
|
|
t * t * t)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Transformation
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Transformation(Stub):
|
|
"""Transformation — 动画变换矩阵"""
|
|
|
|
TYPE_IDENTITY = 0
|
|
TYPE_ALPHA = 1
|
|
TYPE_BOTH = 2
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._alpha: float = 1.0
|
|
self._matrix: Any = None # 占位,实际 Android 为 Matrix 对象
|
|
self._transformation_type: int = Transformation.TYPE_IDENTITY
|
|
|
|
def getAlpha(self) -> float:
|
|
return self._alpha
|
|
|
|
def setAlpha(self, alpha: float):
|
|
self._alpha = alpha
|
|
|
|
def getMatrix(self) -> Any:
|
|
return self._matrix
|
|
|
|
def setMatrix(self, matrix: Any):
|
|
self._matrix = matrix
|
|
|
|
def setTransformationType(self, transformation_type: int):
|
|
self._transformation_type = transformation_type
|
|
|
|
def getTransformationType(self) -> int:
|
|
return self._transformation_type
|
|
|
|
def clear(self):
|
|
self._alpha = 1.0
|
|
self._matrix = None
|
|
self._transformation_type = Transformation.TYPE_IDENTITY
|
|
|
|
def compose(self, other: "Transformation"):
|
|
if other is None:
|
|
return
|
|
self._alpha *= other._alpha
|
|
if self._matrix is None:
|
|
self._matrix = other._matrix
|
|
elif other._matrix is not None:
|
|
pass # 占位:矩阵乘法
|
|
self._transformation_type = max(self._transformation_type,
|
|
other._transformation_type)
|
|
|
|
def toString(self) -> str:
|
|
return (f"Transformation(alpha={self._alpha}, "
|
|
f"type={self._transformation_type})")
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Animation
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Animation(Stub):
|
|
"""Animation — 动画基类"""
|
|
|
|
INFINITE = -1
|
|
RESTART = 1
|
|
REVERSE = 2
|
|
|
|
ABSOLUTE = 0
|
|
RELATIVE_TO_SELF = 1
|
|
RELATIVE_TO_PARENT = 2
|
|
|
|
START_ON_FIRST_FRAME = -1
|
|
|
|
class AnimationListener(Stub):
|
|
"""AnimationListener — 动画监听器"""
|
|
|
|
def onAnimationStart(self, animation: "Animation"):
|
|
pass
|
|
|
|
def onAnimationEnd(self, animation: "Animation"):
|
|
pass
|
|
|
|
def onAnimationRepeat(self, animation: "Animation"):
|
|
pass
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._duration: int = 0
|
|
self._start_offset: int = 0
|
|
self._repeat_count: int = 0
|
|
self._repeat_mode: int = Animation.RESTART
|
|
self._fill_before: bool = False
|
|
self._fill_after: bool = False
|
|
self._fill_enabled: bool = False
|
|
self._z_adjustment: int = 0
|
|
self._interpolator: Optional[Interpolator] = None
|
|
self._listener: Optional["Animation.AnimationListener"] = None
|
|
self._start_time: Optional[int] = None
|
|
self._started: bool = False
|
|
self._ended: bool = False
|
|
self._cycle: float = 0.0
|
|
self._current_repeat: int = 0
|
|
self._initialized: bool = False
|
|
self._scale_factor: float = 1.0
|
|
self._paused: bool = False
|
|
self._more: bool = True
|
|
|
|
def start(self):
|
|
self._start_time = self._current_time_millis()
|
|
self._started = True
|
|
self._ended = False
|
|
self._current_repeat = 0
|
|
self._more = True
|
|
if self._listener:
|
|
self._listener.onAnimationStart(self)
|
|
|
|
def startNow(self):
|
|
self.start()
|
|
|
|
def cancel(self):
|
|
self._ended = True
|
|
self._more = False
|
|
|
|
def reset(self):
|
|
self._start_time = None
|
|
self._started = False
|
|
self._ended = False
|
|
self._current_repeat = 0
|
|
self._cycle = 0.0
|
|
self._more = True
|
|
self._initialized = False
|
|
|
|
def setDuration(self, duration_millis: int):
|
|
self._duration = duration_millis
|
|
|
|
def getDuration(self) -> int:
|
|
return self._duration
|
|
|
|
def setStartOffset(self, start_offset: int):
|
|
self._start_offset = start_offset
|
|
|
|
def getStartOffset(self) -> int:
|
|
return self._start_offset
|
|
|
|
def setRepeatCount(self, repeat_count: int):
|
|
self._repeat_count = repeat_count
|
|
|
|
def getRepeatCount(self) -> int:
|
|
return self._repeat_count
|
|
|
|
def setRepeatMode(self, repeat_mode: int):
|
|
self._repeat_mode = repeat_mode
|
|
|
|
def getRepeatMode(self) -> int:
|
|
return self._repeat_mode
|
|
|
|
def setInterpolator(self, interpolator: Optional[Interpolator]):
|
|
self._interpolator = interpolator
|
|
|
|
def getInterpolator(self) -> Optional[Interpolator]:
|
|
return self._interpolator
|
|
|
|
def setAnimationListener(self, listener: "Animation.AnimationListener"):
|
|
self._listener = listener
|
|
|
|
def getAnimationListener(self) -> Optional["Animation.AnimationListener"]:
|
|
return self._listener
|
|
|
|
def getTransformation(self, current_time: int,
|
|
out_transformation: Transformation) -> bool:
|
|
if self._start_time is None:
|
|
self._start_time = current_time
|
|
|
|
elapsed = current_time - self._start_time - self._start_offset
|
|
if elapsed < 0:
|
|
out_transformation.clear()
|
|
return True
|
|
|
|
duration = self._duration
|
|
if duration == 0:
|
|
duration = 1
|
|
|
|
normalized = elapsed / duration
|
|
|
|
if normalized >= 1.0:
|
|
if self._current_repeat < self._repeat_count or self._repeat_count == self.INFINITE:
|
|
if self._repeat_mode == Animation.REVERSE:
|
|
normalized = 0.0
|
|
else:
|
|
normalized = 0.0
|
|
self._current_repeat += 1
|
|
if self._listener:
|
|
self._listener.onAnimationRepeat(self)
|
|
else:
|
|
normalized = 1.0
|
|
self._ended = True
|
|
self._more = False
|
|
self._apply_transformation(1.0, out_transformation)
|
|
if self._listener:
|
|
self._listener.onAnimationEnd(self)
|
|
return False
|
|
|
|
if normalized > 1.0:
|
|
normalized = 1.0
|
|
|
|
if self._interpolator:
|
|
interpolated = self._interpolator.getInterpolation(normalized)
|
|
else:
|
|
interpolated = normalized
|
|
|
|
self._cycle = normalized
|
|
self._apply_transformation(interpolated, out_transformation)
|
|
return self._more
|
|
|
|
def _apply_transformation(self, interpolated_time: float, t: Transformation):
|
|
pass
|
|
|
|
def hasEnded(self) -> bool:
|
|
return self._ended
|
|
|
|
def hasStarted(self) -> bool:
|
|
return self._started
|
|
|
|
def setFillBefore(self, fill_before: bool):
|
|
self._fill_before = fill_before
|
|
|
|
def getFillBefore(self) -> bool:
|
|
return self._fill_before
|
|
|
|
def setFillAfter(self, fill_after: bool):
|
|
self._fill_after = fill_after
|
|
|
|
def getFillAfter(self) -> bool:
|
|
return self._fill_after
|
|
|
|
def setFillEnabled(self, fill_enabled: bool):
|
|
self._fill_enabled = fill_enabled
|
|
|
|
def getFillEnabled(self) -> bool:
|
|
return self._fill_enabled
|
|
|
|
def setZAdjustment(self, z_adjustment: int):
|
|
self._z_adjustment = z_adjustment
|
|
|
|
def getZAdjustment(self) -> int:
|
|
return self._z_adjustment
|
|
|
|
def willChangeBounds(self) -> bool:
|
|
return True
|
|
|
|
def willChangeTransformationMatrix(self) -> bool:
|
|
return True
|
|
|
|
def initialize(self, width: int, height: int, parent_width: int, parent_height: int):
|
|
self._initialized = True
|
|
|
|
def restrictDuration(self, duration_millis: int):
|
|
if self._duration > duration_millis:
|
|
self._duration = duration_millis
|
|
|
|
def scaleCurrentDuration(self, scale: float):
|
|
self._duration = int(self._duration * scale)
|
|
|
|
@staticmethod
|
|
def _current_time_millis() -> int:
|
|
return int(time.time() * 1000)
|
|
|
|
def toString(self) -> str:
|
|
return (f"Animation(duration={self._duration}, "
|
|
f"repeatCount={self._repeat_count}, "
|
|
f"started={self._started})")
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AlphaAnimation
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AlphaAnimation(Animation):
|
|
"""AlphaAnimation — 透明度动画"""
|
|
|
|
def __init__(self, from_alpha: float, to_alpha: float):
|
|
super().__init__()
|
|
self._from_alpha = from_alpha
|
|
self._to_alpha = to_alpha
|
|
|
|
def _apply_transformation(self, interpolated_time: float, t: Transformation):
|
|
alpha = self._from_alpha + (self._to_alpha - self._from_alpha) * interpolated_time
|
|
t.setAlpha(alpha)
|
|
|
|
def getFromAlpha(self) -> float:
|
|
return self._from_alpha
|
|
|
|
def getToAlpha(self) -> float:
|
|
return self._to_alpha
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# ScaleAnimation
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class ScaleAnimation(Animation):
|
|
"""ScaleAnimation — 缩放动画"""
|
|
|
|
def __init__(self, from_x: float, to_x: float, from_y: float, to_y: float,
|
|
pivot_x: float = 0.0, pivot_y: float = 0.0,
|
|
pivot_x_type: int = Animation.ABSOLUTE,
|
|
pivot_y_type: int = Animation.ABSOLUTE):
|
|
super().__init__()
|
|
self._from_x = from_x
|
|
self._to_x = to_x
|
|
self._from_y = from_y
|
|
self._to_y = to_y
|
|
self._pivot_x = pivot_x
|
|
self._pivot_y = pivot_y
|
|
self._pivot_x_type = pivot_x_type
|
|
self._pivot_y_type = pivot_y_type
|
|
|
|
def _apply_transformation(self, interpolated_time: float, t: Transformation):
|
|
sx = self._from_x + (self._to_x - self._from_x) * interpolated_time
|
|
sy = self._from_y + (self._to_y - self._from_y) * interpolated_time
|
|
# 占位:Matrix 的缩放操作
|
|
if t.getMatrix() is not None:
|
|
pass
|
|
|
|
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
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# TranslateAnimation
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class TranslateAnimation(Animation):
|
|
"""TranslateAnimation — 平移动画"""
|
|
|
|
def __init__(self, from_x: float, to_x: float, from_y: float, to_y: float,
|
|
from_x_type: int = Animation.ABSOLUTE,
|
|
to_x_type: int = Animation.ABSOLUTE,
|
|
from_y_type: int = Animation.ABSOLUTE,
|
|
to_y_type: int = Animation.ABSOLUTE):
|
|
super().__init__()
|
|
self._from_x = from_x
|
|
self._to_x = to_x
|
|
self._from_y = from_y
|
|
self._to_y = to_y
|
|
self._from_x_type = from_x_type
|
|
self._to_x_type = to_x_type
|
|
self._from_y_type = from_y_type
|
|
self._to_y_type = to_y_type
|
|
|
|
def _apply_transformation(self, interpolated_time: float, t: Transformation):
|
|
dx = self._from_x + (self._to_x - self._from_x) * interpolated_time
|
|
dy = self._from_y + (self._to_y - self._from_y) * interpolated_time
|
|
if t.getMatrix() is not None:
|
|
pass
|
|
|
|
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
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# RotateAnimation
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class RotateAnimation(Animation):
|
|
"""RotateAnimation — 旋转动画"""
|
|
|
|
def __init__(self, from_degrees: float, to_degrees: float,
|
|
pivot_x: float = 0.0, pivot_y: float = 0.0,
|
|
pivot_x_type: int = Animation.ABSOLUTE,
|
|
pivot_y_type: int = Animation.ABSOLUTE):
|
|
super().__init__()
|
|
self._from_degrees = from_degrees
|
|
self._to_degrees = to_degrees
|
|
self._pivot_x = pivot_x
|
|
self._pivot_y = pivot_y
|
|
self._pivot_x_type = pivot_x_type
|
|
self._pivot_y_type = pivot_y_type
|
|
|
|
def _apply_transformation(self, interpolated_time: float, t: Transformation):
|
|
degrees = self._from_degrees + (self._to_degrees - self._from_degrees) * interpolated_time
|
|
if t.getMatrix() is not None:
|
|
pass
|
|
|
|
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
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AnimationSet
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AnimationSet(Animation):
|
|
"""AnimationSet — 动画集合"""
|
|
|
|
def __init__(self, share_interpolator: bool = True):
|
|
super().__init__()
|
|
self._share_interpolator = share_interpolator
|
|
self._animations: List[Animation] = []
|
|
|
|
def addAnimation(self, animation: Animation):
|
|
self._animations.append(animation)
|
|
if self._share_interpolator and self._interpolator:
|
|
animation.setInterpolator(self._interpolator)
|
|
|
|
def getAnimations(self) -> List[Animation]:
|
|
return list(self._animations)
|
|
|
|
def setDuration(self, duration_millis: int):
|
|
super().setDuration(duration_millis)
|
|
for anim in self._animations:
|
|
anim.setDuration(duration_millis)
|
|
|
|
def setFillAfter(self, fill_after: bool):
|
|
super().setFillAfter(fill_after)
|
|
for anim in self._animations:
|
|
anim.setFillAfter(fill_after)
|
|
|
|
def setFillBefore(self, fill_before: bool):
|
|
super().setFillBefore(fill_before)
|
|
for anim in self._animations:
|
|
anim.setFillBefore(fill_before)
|
|
|
|
def setRepeatMode(self, repeat_mode: int):
|
|
super().setRepeatMode(repeat_mode)
|
|
for anim in self._animations:
|
|
anim.setRepeatMode(repeat_mode)
|
|
|
|
def setRepeatCount(self, repeat_count: int):
|
|
super().setRepeatCount(repeat_count)
|
|
for anim in self._animations:
|
|
anim.setRepeatCount(repeat_count)
|
|
|
|
def setStartOffset(self, start_offset: int):
|
|
super().setStartOffset(start_offset)
|
|
for anim in self._animations:
|
|
anim.setStartOffset(start_offset)
|
|
|
|
def setInterpolator(self, interpolator: Optional[Interpolator]):
|
|
super().setInterpolator(interpolator)
|
|
if self._share_interpolator:
|
|
for anim in self._animations:
|
|
anim.setInterpolator(interpolator)
|
|
|
|
def start(self):
|
|
super().start()
|
|
for anim in self._animations:
|
|
anim.start()
|
|
|
|
def reset(self):
|
|
super().reset()
|
|
for anim in self._animations:
|
|
anim.reset()
|
|
|
|
def cancel(self):
|
|
super().cancel()
|
|
for anim in self._animations:
|
|
anim.cancel()
|
|
|
|
def getTransformation(self, current_time: int,
|
|
out_transformation: Transformation) -> bool:
|
|
more = False
|
|
for anim in self._animations:
|
|
local_t = Transformation()
|
|
if anim.getTransformation(current_time, local_t):
|
|
more = True
|
|
out_transformation.compose(local_t)
|
|
return more
|
|
|
|
def initialize(self, width: int, height: int, parent_width: int, parent_height: int):
|
|
super().initialize(width, height, parent_width, parent_height)
|
|
for anim in self._animations:
|
|
anim.initialize(width, height, parent_width, parent_height)
|
|
|
|
def willChangeBounds(self) -> bool:
|
|
for anim in self._animations:
|
|
if anim.willChangeBounds():
|
|
return True
|
|
return False
|
|
|
|
def willChangeTransformationMatrix(self) -> bool:
|
|
for anim in self._animations:
|
|
if anim.willChangeTransformationMatrix():
|
|
return True
|
|
return False
|
|
|
|
def getAnimations(self) -> List[Animation]:
|
|
return list(self._animations)
|
|
|
|
def restrictDuration(self, duration_millis: int):
|
|
super().restrictDuration(duration_millis)
|
|
for anim in self._animations:
|
|
anim.restrictDuration(duration_millis)
|
|
|
|
def scaleCurrentDuration(self, scale: float):
|
|
super().scaleCurrentDuration(scale)
|
|
for anim in self._animations:
|
|
anim.scaleCurrentDuration(scale)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Animation.AnimationListener (已定义在 Animation 类中)
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
# Animation.AnimationListener is defined inside the Animation class above
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AnimationUtils
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AnimationUtils(Stub):
|
|
"""AnimationUtils — 动画工具类"""
|
|
|
|
@staticmethod
|
|
def loadAnimation(context: Any, id: int) -> Animation:
|
|
"""加载动画资源(占位实现)"""
|
|
sys.stderr.write(f"[AnimationUtils] loadAnimation: id={id}\n")
|
|
return Animation()
|
|
|
|
@staticmethod
|
|
def makeInAnimation(context: Any, from_left: bool) -> Animation:
|
|
"""创建进入动画"""
|
|
anim = AlphaAnimation(0.0, 1.0)
|
|
anim.setDuration(300)
|
|
return anim
|
|
|
|
@staticmethod
|
|
def makeOutAnimation(context: Any, to_left: bool) -> Animation:
|
|
"""创建退出动画"""
|
|
anim = AlphaAnimation(1.0, 0.0)
|
|
anim.setDuration(300)
|
|
return anim
|
|
|
|
@staticmethod
|
|
def makeInChildBottomAnimation(context: Any) -> Animation:
|
|
"""创建子视图从底部进入动画"""
|
|
anim = TranslateAnimation(0, 0, 1.0, 0.0,
|
|
from_x_type=Animation.RELATIVE_TO_SELF,
|
|
to_x_type=Animation.RELATIVE_TO_SELF,
|
|
from_y_type=Animation.RELATIVE_TO_SELF,
|
|
to_y_type=Animation.RELATIVE_TO_SELF)
|
|
anim.setDuration(300)
|
|
return anim
|
|
|
|
@staticmethod
|
|
def currentAnimationTimeMillis() -> int:
|
|
return int(time.time() * 1000)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# LayoutAnimationController
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class LayoutAnimationController(Stub):
|
|
"""LayoutAnimationController — 布局动画控制器"""
|
|
|
|
ORDER_NORMAL = 0
|
|
ORDER_REVERSE = 1
|
|
ORDER_RANDOM = 2
|
|
|
|
def __init__(self, animation: Optional[Animation] = None):
|
|
super().__init__()
|
|
self._animation: Optional[Animation] = animation
|
|
self._delay: float = 0.5
|
|
self._order: int = LayoutAnimationController.ORDER_NORMAL
|
|
self._initialized: bool = False
|
|
self._max_delay: int = 0
|
|
|
|
def setAnimation(self, animation: Animation):
|
|
self._animation = animation
|
|
|
|
def getAnimation(self) -> Optional[Animation]:
|
|
return self._animation
|
|
|
|
def setOrder(self, order: int):
|
|
self._order = order
|
|
|
|
def getOrder(self) -> int:
|
|
return self._order
|
|
|
|
def setDelay(self, delay: float):
|
|
self._delay = delay
|
|
|
|
def getDelay(self) -> float:
|
|
return self._delay
|
|
|
|
def getDelayForView(self, view: Any) -> int:
|
|
if self._order == LayoutAnimationController.ORDER_NORMAL:
|
|
return 0
|
|
elif self._order == LayoutAnimationController.ORDER_REVERSE:
|
|
return 0
|
|
elif self._order == LayoutAnimationController.ORDER_RANDOM:
|
|
import random
|
|
return int(random.random() * self._max_delay)
|
|
return 0
|
|
|
|
def willOverlap(self) -> bool:
|
|
return self._delay < 1.0
|
|
|
|
def start(self):
|
|
self._initialized = True
|
|
|
|
def isDone(self) -> bool:
|
|
return True
|
|
|
|
def getAnimationForView(self, view: Any) -> Optional[Animation]:
|
|
return self._animation
|
|
|
|
def setInitialized(self, initialized: bool):
|
|
self._initialized = initialized
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# GridLayoutAnimationController
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class GridLayoutAnimationController(LayoutAnimationController):
|
|
"""GridLayoutAnimationController — 网格布局动画控制器"""
|
|
|
|
DIRECTION_LEFT_TO_RIGHT = 0x0
|
|
DIRECTION_RIGHT_TO_LEFT = 0x1
|
|
DIRECTION_TOP_TO_BOTTOM = 0x2
|
|
DIRECTION_BOTTOM_TO_TOP = 0x3
|
|
DIRECTION_HORIZONTAL_MASK = 0x1
|
|
DIRECTION_VERTICAL_MASK = 0x2
|
|
|
|
def __init__(self, animation: Optional[Animation] = None):
|
|
super().__init__(animation)
|
|
self._column_delay: float = 0.5
|
|
self._row_delay: float = 0.5
|
|
self._direction: int = GridLayoutAnimationController.DIRECTION_LEFT_TO_RIGHT
|
|
self._columns: int = 1
|
|
self._rows: int = 1
|
|
|
|
def setColumnDelay(self, column_delay: float):
|
|
self._column_delay = column_delay
|
|
|
|
def getColumnDelay(self) -> float:
|
|
return self._column_delay
|
|
|
|
def setRowDelay(self, row_delay: float):
|
|
self._row_delay = row_delay
|
|
|
|
def getRowDelay(self) -> float:
|
|
return self._row_delay
|
|
|
|
def setDirection(self, direction: int):
|
|
self._direction = direction
|
|
|
|
def getDirection(self) -> int:
|
|
return self._direction
|
|
|
|
def setColumns(self, columns: int):
|
|
self._columns = columns
|
|
|
|
def getColumns(self) -> int:
|
|
return self._columns
|
|
|
|
def setRows(self, rows: int):
|
|
self._rows = rows
|
|
|
|
def getRows(self) -> int:
|
|
return self._rows
|
|
|
|
def getDelayForView(self, view: Any) -> int:
|
|
duration = 0
|
|
if self._animation:
|
|
duration = self._animation.getDuration()
|
|
return int(duration * self._column_delay)
|
|
|
|
def willOverlap(self) -> bool:
|
|
return self._column_delay < 1.0 or self._row_delay < 1.0
|
|
|
|
def getAnimationForView(self, view: Any) -> Optional[Animation]:
|
|
return self._animation
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# __all__
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
__all__ = [
|
|
"Animation",
|
|
"AnimationSet",
|
|
"AlphaAnimation",
|
|
"ScaleAnimation",
|
|
"TranslateAnimation",
|
|
"RotateAnimation",
|
|
"AnimationUtils",
|
|
"Interpolator",
|
|
"AccelerateInterpolator",
|
|
"DecelerateInterpolator",
|
|
"AccelerateDecelerateInterpolator",
|
|
"LinearInterpolator",
|
|
"OvershootInterpolator",
|
|
"AnticipateInterpolator",
|
|
"AnticipateOvershootInterpolator",
|
|
"BounceInterpolator",
|
|
"CycleInterpolator",
|
|
"PathInterpolator",
|
|
"Transformation",
|
|
"LayoutAnimationController",
|
|
"GridLayoutAnimationController",
|
|
] |