Files
Android-pyApi/android_pyapi/android/graphics_drawable.py
T
shuiyu1123 bfe27fdc08 feat: Android-pyApi v1.0 - 全面翻译 Android SDK API 为 Python
52 个模块覆盖 Android + Java + org.json 标准库
2026-06-24 11:16:16 +00:00

1128 lines
38 KiB
Python

"""
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",
]