""" Android Graphics Drawable 组件 — Drawable, BitmapDrawable, ColorDrawable 等 方法签名完全对齐 Android SDK """ import math from typing import Any, Dict, List, Optional, Sequence, Tuple, Union from .._core import Stub, AndroidOnlyError, android_context, HAS_PIL, resolve_android_path from .graphics import ( Bitmap, Canvas, Color, ColorFilter, Paint, PorterDuff, PorterDuffColorFilter, Rect, RectF, Matrix, Path, Shader, Rect, BlendMode, BlendModeColorFilter, ) # ═══════════════════════════════════════════════════════════════ # ColorStateList # ═══════════════════════════════════════════════════════════════ class ColorStateList(Stub): """ColorStateList — 颜色状态列表""" def __init__(self, states: List[List[int]], colors: List[int]): super().__init__() self._states = list(states) self._colors = list(colors) self._default_color = colors[-1] if colors else 0 def getColorForState(self, stateSet: List[int], defaultColor: int = 0) -> int: return self._default_color def getDefaultColor(self) -> int: return self._default_color def isStateful(self) -> bool: return len(self._states) > 0 def withAlpha(self, alpha: int) -> "ColorStateList": new_colors = [] for c in self._colors: a = Color.alpha(c) new_a = int(a * alpha / 255) new_colors.append(Color.argb(new_a, Color.red(c), Color.green(c), Color.blue(c))) return ColorStateList(self._states, new_colors) @staticmethod def valueOf(color: int) -> "ColorStateList": return ColorStateList([[]], [color]) def toString(self) -> str: return f"ColorStateList({len(self._states)} states)" # ═══════════════════════════════════════════════════════════════ # Drawable # ═══════════════════════════════════════════════════════════════ class Drawable(Stub): """Drawable — 可绘制对象的抽象基类""" def __init__(self): super().__init__() self._bounds = Rect(0, 0, 0, 0) self._alpha = 255 self._color_filter = None self._callback = None self._visible = True self._level = 0 self._intrinsic_width = -1 self._intrinsic_height = -1 def setBounds(self, left: int, top: int, right: int, bottom: int): self._bounds.set(left, top, right, bottom) def setBounds(self, bounds: Rect): self._bounds.set(bounds.left, bounds.top, bounds.right, bounds.bottom) def getBounds(self) -> Rect: return self._bounds def copyBounds(self) -> Rect: return Rect(self._bounds.left, self._bounds.top, self._bounds.right, self._bounds.bottom) def copyBounds(self, out: Rect) -> Rect: out.set(self._bounds.left, self._bounds.top, self._bounds.right, self._bounds.bottom) return out def getIntrinsicWidth(self) -> int: return self._intrinsic_width def getIntrinsicHeight(self) -> int: return self._intrinsic_height def setAlpha(self, alpha: int): self._alpha = alpha def getAlpha(self) -> int: return self._alpha def setColorFilter(self, colorFilter: Optional[ColorFilter]): self._color_filter = colorFilter def getColorFilter(self) -> Optional[ColorFilter]: return self._color_filter def getOpacity(self) -> int: return 0 # PixelFormat.UNKNOWN def invalidateSelf(self): if self._callback: self._callback.invalidateDrawable(self) def setCallback(self, cb): self._callback = cb def getCallback(self): return self._callback def scheduleDrawable(self, what, who, when: int): if self._callback: self._callback.scheduleDrawable(what, who, when) def unscheduleDrawable(self, what, who): if self._callback: self._callback.unscheduleDrawable(what, who) def draw(self, canvas: Canvas): pass def setVisible(self, visible: bool, restart: bool = False) -> bool: old = self._visible self._visible = visible return old def isVisible(self) -> bool: return self._visible def setLevel(self, level: int) -> bool: self._level = level return True def getLevel(self) -> int: return self._level def getConstantState(self) -> Optional["DrawableConstantState"]: return None def getCurrent(self) -> "Drawable": return self def mutate(self) -> "Drawable": return self def getMinimumWidth(self) -> int: return 0 def getMinimumHeight(self) -> int: return 0 def setDither(self, dither: bool): pass def setFilterBitmap(self, filter: bool): pass def setHotspot(self, x: float, y: float): pass def setHotspotBounds(self, left: int, top: int, right: int, bottom: int): pass def getHotspotBounds(self, outRect: Rect): outRect.set(0, 0, 0, 0) def isStateful(self) -> bool: return False def setState(self, stateSet: List[int]) -> bool: return False def getState(self) -> List[int]: return [] def jumpToCurrentState(self): pass def clearColorFilter(self): self._color_filter = None def getTransparentRegion(self): from .graphics import Region return Region(self._bounds) def onBoundsChange(self, bounds: Rect): pass def onLevelChange(self, level: int) -> bool: return False def onStateChange(self, stateSet: List[int]) -> bool: return False def toString(self) -> str: return f"{self.__class__.__name__}({self._bounds})" class DrawableConstantState(Stub): """DrawableConstantState — Drawable 常量状态""" pass # ═══════════════════════════════════════════════════════════════ # BitmapDrawable # ═══════════════════════════════════════════════════════════════ class BitmapDrawable(Drawable): """BitmapDrawable — 位图 Drawable""" def __init__(self, res=None, bitmap: Bitmap = None): super().__init__() self._bitmap = bitmap if bitmap else Bitmap(0, 0, Bitmap.Config.ARGB_8888) self._paint = Paint() self._target_density = 160 self._gravity = 0x33 # Gravity.FILL self._tile_mode_x = None self._tile_mode_y = None self._mutated = False if self._bitmap: self._intrinsic_width = self._bitmap.getWidth() self._intrinsic_height = self._bitmap.getHeight() def getBitmap(self) -> Bitmap: return self._bitmap def getPaint(self) -> Paint: return self._paint def setAntiAlias(self, aa: bool): self._paint.setAntiAlias(aa) def setFilterBitmap(self, filter: bool): self._paint.setFilterBitmap(filter) def setDither(self, dither: bool): self._paint.setDither(dither) def setTileModeX(self, mode): self._tile_mode_x = mode def setTileModeY(self, mode): self._tile_mode_y = mode def getTileModeX(self): return self._tile_mode_x def getTileModeY(self): return self._tile_mode_y def setGravity(self, gravity: int): self._gravity = gravity def getGravity(self) -> int: return self._gravity def setTargetDensity(self, density: int): self._target_density = density def getTargetDensity(self) -> int: return self._target_density def draw(self, canvas: Canvas): if self._bitmap: bounds = self._bounds if HAS_PIL and hasattr(canvas, "_image") and hasattr(self._bitmap, "_image"): bm_img = self._bitmap._image if bm_img: resized = bm_img.resize((bounds.width(), bounds.height())) canvas._image.paste(resized, (bounds.left, bounds.top)) def getIntrinsicWidth(self) -> int: return self._bitmap.getWidth() if self._bitmap else -1 def getIntrinsicHeight(self) -> int: return self._bitmap.getHeight() if self._bitmap else -1 def getOpacity(self) -> int: if self._bitmap: return 1 if self._bitmap.hasAlpha() else -1 return 0 def mutate(self) -> "BitmapDrawable": if not self._mutated: self._bitmap = Bitmap.createBitmap(self._bitmap) self._paint = Paint() self._mutated = True return self # ═══════════════════════════════════════════════════════════════ # ColorDrawable # ═══════════════════════════════════════════════════════════════ class ColorDrawable(Drawable): """ColorDrawable — 纯色 Drawable""" def __init__(self, color: int = 0): super().__init__() self._color = color self._paint = Paint() self._paint.setColor(color) def getColor(self) -> int: return self._color def setColor(self, color: int): self._color = color self._paint.setColor(color) def setAlpha(self, alpha: int): self._paint.setAlpha(alpha) def getAlpha(self) -> int: return self._paint.getAlpha() def draw(self, canvas: Canvas): canvas.drawRect(self._bounds, self._paint) def getOpacity(self) -> int: if Color.alpha(self._color) == 255: return -1 # OPAQUE return -3 # TRANSLUCENT def mutate(self) -> "ColorDrawable": self._paint = Paint() self._paint.setColor(self._color) return self # ═══════════════════════════════════════════════════════════════ # GradientDrawable # ═══════════════════════════════════════════════════════════════ class GradientDrawable(Drawable): """GradientDrawable — 渐变 Drawable""" RECTANGLE = 0 OVAL = 1 LINE = 2 RING = 3 class Orientation: TOP_BOTTOM = 0 TR_BL = 1 RIGHT_LEFT = 2 BR_TL = 3 BOTTOM_TOP = 4 BL_TR = 5 LEFT_RIGHT = 6 TL_BR = 7 def __init__(self, orientation: int = Orientation.TOP_BOTTOM, colors: List[int] = None): super().__init__() self._orientation = orientation self._colors = colors if colors else [Color.BLACK, Color.WHITE] self._shape = GradientDrawable.RECTANGLE self._corner_radius = 0.0 self._corner_radii = None self._stroke_width = 0 self._stroke_color = 0 self._gradient_type = 0 # LINEAR_GRADIENT self._gradient_center_x = 0.5 self._gradient_center_y = 0.5 self._use_level = False self._width = -1 self._height = -1 self._color = None def setShape(self, shape: int): self._shape = shape def getShape(self) -> int: return self._shape def setSize(self, width: int, height: int): self._width = width self._height = height def setStroke(self, width: int, color: int, dashWidth: float = 0, dashGap: float = 0): self._stroke_width = width self._stroke_color = color def setCornerRadius(self, radius: float): self._corner_radius = radius self._corner_radii = None def setCornerRadii(self, radii: List[float]): self._corner_radii = radii self._corner_radius = radii[0] if radii else 0 def setColors(self, colors: List[int]): self._colors = list(colors) self._color = None def setColor(self, color: int): self._color = color self._colors = None def setGradientType(self, gradientType: int): self._gradient_type = gradientType def setGradientCenter(self, x: float, y: float): self._gradient_center_x = x self._gradient_center_y = y def setOrientation(self, orientation: int): self._orientation = orientation def setUseLevel(self, useLevel: bool): self._use_level = useLevel def getIntrinsicWidth(self) -> int: return self._width if self._width > 0 else super().getIntrinsicWidth() def getIntrinsicHeight(self) -> int: return self._height if self._height > 0 else super().getIntrinsicHeight() def draw(self, canvas: Canvas): bounds = self._bounds if HAS_PIL and hasattr(canvas, "_draw") and canvas._draw: if self._color is not None: c = self._color fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) elif self._colors: c = self._colors[0] fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) else: fill_color = (0, 0, 0, 255) if self._shape == GradientDrawable.OVAL: canvas._draw.ellipse([bounds.left, bounds.top, bounds.right, bounds.bottom], fill=fill_color) elif self._shape == GradientDrawable.RECTANGLE and self._corner_radius > 0: canvas._draw.rounded_rectangle( [bounds.left, bounds.top, bounds.right, bounds.bottom], radius=self._corner_radius, fill=fill_color) else: canvas._draw.rectangle([bounds.left, bounds.top, bounds.right, bounds.bottom], fill=fill_color) # ═══════════════════════════════════════════════════════════════ # LayerDrawable # ═══════════════════════════════════════════════════════════════ class LayerDrawable(Drawable): """LayerDrawable — 图层 Drawable""" def __init__(self, layers: List[Drawable]): super().__init__() self._layers = list(layers) self._ids = list(range(len(layers))) self._layer_insets = [Rect(0, 0, 0, 0) for _ in layers] def findDrawableByLayerId(self, id: int) -> Optional[Drawable]: for i, lid in enumerate(self._ids): if lid == id: return self._layers[i] return None def setId(self, index: int, id: int): if 0 <= index < len(self._ids): self._ids[index] = id def getNumberOfLayers(self) -> int: return len(self._layers) def getDrawable(self, index: int) -> Optional[Drawable]: if 0 <= index < len(self._layers): return self._layers[index] return None def setDrawableByLayerId(self, id: int, drawable: Drawable) -> bool: for i, lid in enumerate(self._ids): if lid == id: self._layers[i] = drawable return True return False def setLayerInset(self, index: int, l: int, t: int, r: int, b: int): if 0 <= index < len(self._layer_insets): self._layer_insets[index].set(l, t, r, b) def setLayerInsetLeft(self, index: int, left: int): if 0 <= index < len(self._layer_insets): self._layer_insets[index].left = left def setLayerInsetTop(self, index: int, top: int): if 0 <= index < len(self._layer_insets): self._layer_insets[index].top = top def setLayerInsetRight(self, index: int, right: int): if 0 <= index < len(self._layer_insets): self._layer_insets[index].right = right def setLayerInsetBottom(self, index: int, bottom: int): if 0 <= index < len(self._layer_insets): self._layer_insets[index].bottom = bottom def draw(self, canvas: Canvas): for i, layer in enumerate(self._layers): ins = self._layer_insets[i] if i < len(self._layer_insets) else Rect(0, 0, 0, 0) b = self._bounds layer.setBounds(b.left + ins.left, b.top + ins.top, b.right - ins.right, b.bottom - ins.bottom) layer.draw(canvas) def mutate(self) -> "LayerDrawable": return self def getOpacity(self) -> int: return 0 def isStateful(self) -> bool: return any(d.isStateful() for d in self._layers) def setVisible(self, visible: bool, restart: bool = False) -> bool: old = super().setVisible(visible, restart) for d in self._layers: d.setVisible(visible, restart) return old # ═══════════════════════════════════════════════════════════════ # StateListDrawable # ═══════════════════════════════════════════════════════════════ class StateListDrawable(Drawable): """StateListDrawable — 状态列表 Drawable""" def __init__(self): super().__init__() self._state_sets: List[List[int]] = [] self._drawables: List[Drawable] = [] self._current_index = -1 def addState(self, stateSet: List[int], drawable: Drawable): self._state_sets.append(stateSet) self._drawables.append(drawable) def selectDrawable(self, index: int) -> bool: if 0 <= index < len(self._drawables): self._current_index = index return True return False def getCurrent(self) -> Drawable: if 0 <= self._current_index < len(self._drawables): return self._drawables[self._current_index] if self._drawables: return self._drawables[0] return self def draw(self, canvas: Canvas): d = self.getCurrent() d.setBounds(self._bounds) d.draw(canvas) def isStateful(self) -> bool: return True def setState(self, stateSet: List[int]) -> bool: for i, ss in enumerate(self._state_sets): if not ss or all(s in stateSet for s in ss): self._current_index = i return True return False def mutate(self) -> "StateListDrawable": self._drawables = [d.mutate() for d in self._drawables] return self # ═══════════════════════════════════════════════════════════════ # LevelListDrawable # ═══════════════════════════════════════════════════════════════ class LevelListDrawable(Drawable): """LevelListDrawable — 级别列表 Drawable""" def __init__(self): super().__init__() self._levels: List[Tuple[int, int, Drawable]] = [] self._current_index = -1 def addLevel(self, low: int, high: int, drawable: Drawable): self._levels.append((low, high, drawable)) def onLevelChange(self, level: int) -> bool: for i, (low, high, d) in enumerate(self._levels): if low <= level <= high: self._current_index = i return True return False def getCurrent(self) -> Drawable: if 0 <= self._current_index < len(self._levels): return self._levels[self._current_index][2] if self._levels: return self._levels[0][2] return self def draw(self, canvas: Canvas): d = self.getCurrent() d.setBounds(self._bounds) d.draw(canvas) # ═══════════════════════════════════════════════════════════════ # TransitionDrawable # ═══════════════════════════════════════════════════════════════ class TransitionDrawable(Drawable): """TransitionDrawable — 过渡 Drawable""" def __init__(self, layers: List[Drawable]): super().__init__() self._layers = list(layers) self._transition_progress = 0.0 self._reverse = False def startTransition(self, durationMillis: int): self._transition_progress = 0.0 self._reverse = False def resetTransition(self): self._transition_progress = 0.0 self._reverse = False def reverse(self, durationMillis: int): self._transition_progress = 1.0 self._reverse = True def startReverse(self, durationMillis: int = 0): self._transition_progress = 1.0 self._reverse = True def draw(self, canvas: Canvas): for layer in self._layers: layer.setBounds(self._bounds) layer.draw(canvas) def getNumberOfLayers(self) -> int: return len(self._layers) def getDrawable(self, index: int) -> Optional[Drawable]: if 0 <= index < len(self._layers): return self._layers[index] return None def isCrossFadeEnabled(self) -> bool: return True def setCrossFadeEnabled(self, enabled: bool): pass # ═══════════════════════════════════════════════════════════════ # RotateDrawable # ═══════════════════════════════════════════════════════════════ class RotateDrawable(Drawable): """RotateDrawable — 旋转 Drawable""" def __init__(self): super().__init__() self._drawable = None self._from_degrees = 0.0 self._to_degrees = 360.0 self._pivot_x = 0.5 self._pivot_y = 0.5 self._current_degrees = 0.0 def setDrawable(self, drawable: Drawable): self._drawable = drawable def getDrawable(self) -> Optional[Drawable]: return self._drawable def setFromDegrees(self, fromDegrees: float): self._from_degrees = fromDegrees def setToDegrees(self, toDegrees: float): self._to_degrees = toDegrees def setPivotX(self, pivotX: float): self._pivot_x = pivotX def setPivotY(self, pivotY: float): self._pivot_y = pivotY def draw(self, canvas: Canvas): if self._drawable: self._drawable.setBounds(self._bounds) self._drawable.draw(canvas) # ═══════════════════════════════════════════════════════════════ # ScaleDrawable # ═══════════════════════════════════════════════════════════════ class ScaleDrawable(Drawable): """ScaleDrawable — 缩放 Drawable""" def __init__(self, drawable: Drawable = None, gravity: int = 0x33, scaleWidth: float = 0.0, scaleHeight: float = 0.0): super().__init__() self._drawable = drawable self._gravity = gravity self._scale_width = scaleWidth self._scale_height = scaleHeight self._level = 0 def getDrawable(self) -> Optional[Drawable]: return self._drawable def setLevel(self, level: int) -> bool: self._level = level if self._drawable: self._drawable.setLevel(level) return True def draw(self, canvas: Canvas): if self._drawable: self._drawable.setBounds(self._bounds) self._drawable.draw(canvas) # ═══════════════════════════════════════════════════════════════ # ClipDrawable # ═══════════════════════════════════════════════════════════════ class ClipDrawable(Drawable): """ClipDrawable — 裁剪 Drawable""" HORIZONTAL = 1 VERTICAL = 2 def __init__(self, drawable: Drawable = None, gravity: int = 0x33, orientation: int = HORIZONTAL): super().__init__() self._drawable = drawable self._gravity = gravity self._orientation = orientation self._level = 0 def setLevel(self, level: int) -> bool: self._level = level if self._drawable: self._drawable.setLevel(level) return True def draw(self, canvas: Canvas): if self._drawable: self._drawable.setBounds(self._bounds) self._drawable.draw(canvas) # ═══════════════════════════════════════════════════════════════ # InsetDrawable # ═══════════════════════════════════════════════════════════════ class InsetDrawable(Drawable): """InsetDrawable — 内嵌 Drawable""" def __init__(self, drawable: Drawable, inset: float): super().__init__() self._drawable = drawable self._inset = int(inset) self._inset_left = int(inset) self._inset_top = int(inset) self._inset_right = int(inset) self._inset_bottom = int(inset) def getDrawable(self) -> Optional[Drawable]: return self._drawable def getInset(self) -> int: return self._inset def draw(self, canvas: Canvas): if self._drawable: b = self._bounds self._drawable.setBounds( b.left + self._inset_left, b.top + self._inset_top, b.right - self._inset_right, b.bottom - self._inset_bottom ) self._drawable.draw(canvas) # ═══════════════════════════════════════════════════════════════ # ShapeDrawable # ═══════════════════════════════════════════════════════════════ class ShapeDrawable(Drawable): """ShapeDrawable — 形状 Drawable""" def __init__(self, shape: "Shape" = None): super().__init__() self._shape = shape self._paint = Paint() self._intrinsic_width = -1 self._intrinsic_height = -1 def getShape(self): return self._shape def setShape(self, shape): self._shape = shape def getPaint(self) -> Paint: return self._paint def setIntrinsicWidth(self, width: int): self._intrinsic_width = width def setIntrinsicHeight(self, height: int): self._intrinsic_height = height def getIntrinsicWidth(self) -> int: return self._intrinsic_width if self._intrinsic_width > 0 else super().getIntrinsicWidth() def getIntrinsicHeight(self) -> int: return self._intrinsic_height if self._intrinsic_height > 0 else super().getIntrinsicHeight() def draw(self, canvas: Canvas): if self._shape: self._shape.draw(canvas, self._paint) class Shape(Stub): """Shape — 形状基类""" def draw(self, canvas: Canvas, paint: Paint): pass class ArcShape(Shape): """ArcShape — 弧形""" def __init__(self, startAngle: float, sweepAngle: float): super().__init__() self._start_angle = startAngle self._sweep_angle = sweepAngle class OvalShape(Shape): """OvalShape — 椭圆""" def draw(self, canvas: Canvas, paint: Paint): if HAS_PIL and hasattr(canvas, "_draw"): c = paint.getColor() fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) b = canvas.getClipBounds() canvas._draw.ellipse([b.left, b.top, b.right, b.bottom], fill=fill_color) class RectShape(Shape): """RectShape — 矩形""" def draw(self, canvas: Canvas, paint: Paint): if HAS_PIL and hasattr(canvas, "_draw"): c = paint.getColor() fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) b = canvas.getClipBounds() canvas._draw.rectangle([b.left, b.top, b.right, b.bottom], fill=fill_color) class RoundRectShape(Shape): """RoundRectShape — 圆角矩形""" def __init__(self, outerRadii: List[float], inset: RectF, innerRadii: List[float]): super().__init__() self._outer_radii = outerRadii self._inset = inset self._inner_radii = innerRadii def draw(self, canvas: Canvas, paint: Paint): if HAS_PIL and hasattr(canvas, "_draw"): c = paint.getColor() fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) b = canvas.getClipBounds() radius = self._outer_radii[0] if self._outer_radii else 0 canvas._draw.rounded_rectangle([b.left, b.top, b.right, b.bottom], radius=radius, fill=fill_color) class PaintDrawable(ShapeDrawable): """PaintDrawable — 画笔 Drawable (ShapeDrawable 子类)""" def __init__(self, color: int = 0): super().__init__(RectShape()) self.getPaint().setColor(color) def setCornerRadius(self, radius: float): self.setShape(RoundRectShape([radius, radius, radius, radius], None, None)) # ═══════════════════════════════════════════════════════════════ # VectorDrawable # ═══════════════════════════════════════════════════════════════ class VectorDrawable(Drawable): """VectorDrawable — 矢量 Drawable""" def __init__(self): super().__init__() self._alpha = 255 def setAlpha(self, alpha: int): self._alpha = alpha def getAlpha(self) -> int: return self._alpha def draw(self, canvas: Canvas): pass @staticmethod def createFromXml(res, parser) -> "VectorDrawable": return VectorDrawable() # ═══════════════════════════════════════════════════════════════ # RippleDrawable # ═══════════════════════════════════════════════════════════════ class RippleDrawable(Drawable): """RippleDrawable — 水波纹 Drawable (API 21+)""" def __init__(self, colorStates: ColorStateList, content: Drawable = None, mask: Drawable = None): super().__init__() self._color_states = colorStates self._content = content self._mask = mask self._radius = -1 def setRadius(self, radius: int): self._radius = radius def getRadius(self) -> int: return self._radius def draw(self, canvas: Canvas): if self._content: self._content.setBounds(self._bounds) self._content.draw(canvas) def setColor(self, color: ColorStateList): self._color_states = color def getColor(self) -> Optional[ColorStateList]: return self._color_states def mutate(self) -> "RippleDrawable": return self # ═══════════════════════════════════════════════════════════════ # AnimatedVectorDrawable # ═══════════════════════════════════════════════════════════════ class AnimatedVectorDrawable(Drawable): """AnimatedVectorDrawable — 动画矢量 Drawable (API 21+)""" def __init__(self): super().__init__() self._running = False def start(self): self._running = True def stop(self): self._running = False def isRunning(self) -> bool: return self._running def reset(self): self._running = False def draw(self, canvas: Canvas): pass @staticmethod def createFromXml(res, parser) -> "AnimatedVectorDrawable": return AnimatedVectorDrawable() # ═══════════════════════════════════════════════════════════════ # AnimationDrawable # ═══════════════════════════════════════════════════════════════ class AnimationDrawable(Drawable): """AnimationDrawable — 帧动画 Drawable""" def __init__(self): super().__init__() self._frames: List[Tuple[Drawable, int]] = [] self._current_frame = 0 self._oneshot = False self._running = False def addFrame(self, frame: Drawable, duration: int): self._frames.append((frame, duration)) def getFrame(self, index: int) -> Optional[Drawable]: if 0 <= index < len(self._frames): return self._frames[index][0] return None def getNumberOfFrames(self) -> int: return len(self._frames) def getDuration(self, index: int) -> int: if 0 <= index < len(self._frames): return self._frames[index][1] return 0 def setOneShot(self, oneshot: bool): self._oneshot = oneshot def isOneShot(self) -> bool: return self._oneshot def start(self): self._running = True def stop(self): self._running = False def isRunning(self) -> bool: return self._running def run(self): pass def selectDrawable(self, index: int) -> bool: if 0 <= index < len(self._frames): self._current_frame = index return True return False def getCurrent(self) -> Drawable: if self._frames: return self._frames[self._current_frame][0] return self def draw(self, canvas: Canvas): d = self.getCurrent() d.setBounds(self._bounds) d.draw(canvas) def setVisible(self, visible: bool, restart: bool = False) -> bool: old = super().setVisible(visible, restart) if visible and restart: self._current_frame = 0 return old # ═══════════════════════════════════════════════════════════════ # DrawableContainer # ═══════════════════════════════════════════════════════════════ class DrawableContainer(Drawable): """DrawableContainer — Drawable 容器""" def __init__(self): super().__init__() self._drawables: List[Drawable] = [] self._current_index = 0 def selectDrawable(self, index: int) -> bool: if 0 <= index < len(self._drawables): self._current_index = index return True return False def getCurrent(self) -> Drawable: if self._drawables: return self._drawables[self._current_index] return self def draw(self, canvas: Canvas): d = self.getCurrent() d.setBounds(self._bounds) d.draw(canvas) def setAlpha(self, alpha: int): for d in self._drawables: d.setAlpha(alpha) def setColorFilter(self, cf: Optional[ColorFilter]): for d in self._drawables: d.setColorFilter(cf) def getOpacity(self) -> int: return 0 def isStateful(self) -> bool: return any(d.isStateful() for d in self._drawables) def mutate(self) -> "DrawableContainer": return self # ═══════════════════════════════════════════════════════════════ # __all__ # ═══════════════════════════════════════════════════════════════ __all__ = [ "Drawable", "BitmapDrawable", "ColorDrawable", "GradientDrawable", "LayerDrawable", "StateListDrawable", "LevelListDrawable", "TransitionDrawable", "RotateDrawable", "ScaleDrawable", "ClipDrawable", "InsetDrawable", "ShapeDrawable", "PaintDrawable", "VectorDrawable", "RippleDrawable", "AnimatedVectorDrawable", "AnimationDrawable", "DrawableContainer", "DrawableConstantState", "ColorStateList", # Shapes "Shape", "ArcShape", "OvalShape", "RectShape", "RoundRectShape", ]