Files
Android-pyApi/android_pyapi/android/widget.py
T
shuiyu1123 39b1da37c9 feat: 全面完善API — 补全核心类方法、存根转真实实现、Exception体系扩展
核心类方法补全:
- Activity: +20方法 (requestWindowFeature, setTitle/getTitle, onKeyDown/onKeyUp, enterPictureInPictureMode, getReferrer, isTaskRoot, navigateUpTo等)
- Fragment: +15方法 (getChildFragmentManager, getParentFragment, requireContext/requireActivity, setMenuVisibility等)
- Intent: +20方法 (getParcelableExtra, setComponent/getComponent, getDataString, getScheme, putExtras等)
- View: +20方法 (setLabelFor, setRotationX/Y, setForeground, setBackgroundTintList等)
- KeyEvent: +7方法 (getCharacters, isSystem, isPrintingKey, isModifierKey, startTracking等)
- Paint: +12方法 (setStrokeCap/Join, getTextBounds, setBlendMode, setLetterSpacing等)
- Notification.Builder: +10方法 (setProgress, setShowWhen, setOnlyAlertOnce, setSound等)

存根转真实实现:
- TrafficStats: 读取 /proc/net/dev 返回真实网络流量
- RecyclerView.Adapter: 观察者模式触发通知
- BaseAdapter: 观察者模式触发通知
- ExifInterface: 使用PIL实现图片翻转/旋转
- Process: 使用os.kill实现进程信号
- FileUtils: 真实文件I/O
- SharedPreferences: 监听器模式
- TelephonyManager.listen: 维护监听器列表
- ScrollView.smoothScrollTo: 更新滚动位置

Exception体系扩展:
- 新增15个Exception/Error类 (ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchElementException, AssertionError, OutOfMemoryError, StackOverflowError, NoClassDefFoundError, UnsatisfiedLinkError, LinkageError, VerifyError, AbstractMethodError, IncompatibleClassChangeError, ExceptionInInitializerError)

Math/System/Thread/StringBuilder/Integer增强:
- Math: +20方法 (subtractExact, multiplyExact, toIntExact, floorDiv/floorMod, fma等)
- System: getProperties/setProperties/mapLibraryName/getLogger
- Thread: +10方法 (getUncaughtExceptionHandler, getAllStackTraces, getStackTrace, holdLock等)
- StringBuilder: +7方法 (ensureCapacity, trimToSize, setLength, subSequence等)
- Integer: +8静态方法 (compare, compareUnsigned, divideUnsigned, sum, max, min等)

修复:
- app_fragment.py: 使用java.lang.IllegalStateException替代本地定义
- content_intent.py: Intent.setData/getScheme正确从Uri提取scheme
- view_ext.py: KeyEvent继承view.KeyEvent获取所有KEYCODE常量
- __init__.py: 补全Fragment/KeyEvent/MotionEvent/TrafficStats/RecyclerView等导出

统计: 221模块, 3660类, 30826方法, 180217行
2026-06-24 23:46:36 +00:00

4821 lines
154 KiB
Python

"""
Android Widget 组件 — TextView, Button, RecyclerView, ListView, WebView 等
方法签名完全对齐 Android SDK
"""
import math
import time
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
from .._core import Stub, AndroidOnlyError, android_context
from .view import (
View, ViewGroup, ViewTreeObserver,
MotionEvent, KeyEvent, Gravity, SurfaceView,
ViewGroup as VG,
)
try:
from .._linux_impl import IS_LINUX, LinuxNotify, LinuxClipboard
_USE_LINUX = IS_LINUX
except ImportError:
_USE_LINUX = False
# ═══════════════════════════════════════════════════════════════
# TextView
# ═══════════════════════════════════════════════════════════════
class TextView(View):
"""TextView — 文本显示视图"""
# 自动链接
AUTO_LINK_NONE = 0x00
AUTO_LINK_WEB_URLS = 0x01
AUTO_LINK_EMAIL_ADDRESSES = 0x02
AUTO_LINK_PHONE_NUMBERS = 0x04
AUTO_LINK_MAP_ADDRESSES = 0x08
AUTO_LINK_ALL = 0x0F
# Buffer类型
BUFFER_TYPE_NORMAL = 0
BUFFER_TYPE_SPANNABLE = 1
BUFFER_TYPE_EDITABLE = 2
# 截断位置
TRUNCATE_AT_END = 0
TRUNCATE_AT_MIDDLE = 1
TRUNCATE_AT_START = 2
# 文本方向
TEXT_DIRECTION_INHERIT = 0
TEXT_DIRECTION_FIRST_STRONG = 1
TEXT_DIRECTION_ANY_RTL = 2
TEXT_DIRECTION_LTR = 3
TEXT_DIRECTION_RTL = 4
TEXT_DIRECTION_LOCALE = 5
TEXT_DIRECTION_FIRST_STRONG_LTR = 6
TEXT_DIRECTION_FIRST_STRONG_RTL = 7
# 文本对齐
TEXT_ALIGNMENT_INHERIT = 0
TEXT_ALIGNMENT_GRAVITY = 1
TEXT_ALIGNMENT_TEXT_START = 2
TEXT_ALIGNMENT_TEXT_END = 3
TEXT_ALIGNMENT_CENTER = 4
TEXT_ALIGNMENT_VIEW_START = 5
TEXT_ALIGNMENT_VIEW_END = 6
def __init__(self, context=None):
super().__init__(context)
self._text: str = ""
self._hint: str = ""
self._text_size: float = 14.0
self._text_color: int = 0xFF000000
self._hint_text_color: int = 0xFF808080
self._link_text_color: int = 0xFFFF0000
self._highlight_color: int = 0x556633CC
self._gravity = Gravity.NO_GRAVITY
self._typeface: Any = None
self._input_type: int = 0
self._lines: int = 0
self._max_lines: int = 0
self._min_lines: int = 0
self._single_line: bool = False
self._ellipsize: Optional[str] = None # "end", "start", "middle", "marquee"
self._movement_method: Any = None
self._auto_link_mask: int = TextView.AUTO_LINK_NONE
self._transformation_method: Optional[Callable] = None
self._shadow_radius: float = 0.0
self._shadow_dx: float = 0.0
self._shadow_dy: float = 0.0
self._shadow_color: int = 0
self._line_spacing_extra: float = 0.0
self._line_spacing_mult: float = 1.0
self._compound_drawables: List[Any] = [None, None, None, None]
self._compound_drawable_padding: int = 0
self._include_font_padding: bool = True
self._elegant_text_height: bool = False
self._letter_spacing: float = 0.0
self._font_feature_settings: str = ""
self._key_listener: Any = None
self._filters: List[Any] = []
self._select_all_on_focus: bool = False
self._horizontally_scrolling: bool = False
self._on_editor_action_listener: Optional[Callable] = None
self._paint: Any = None
self._line_count: int = 1
self._layout: Any = None
self._links_clickable: bool = False
self._cursor_visible: bool = True
self._editable: bool = False
self._selection_start: int = 0
self._selection_end: int = 0
self._scroll_horizontally: bool = False
# ─── text ──────────────────────────────────────────────────
def setText(self, text: str, buffer_type: int = BUFFER_TYPE_NORMAL):
self._text = str(text) if text is not None else ""
self._line_count = self._text.count("\n") + 1 if self._text else 0
def getText(self) -> str:
return self._text
def append(self, text: str):
self._text += str(text) if text is not None else ""
self._line_count = self._text.count("\n") + 1 if self._text else 0
# ─── text size / color ─────────────────────────────────────
def setTextSize(self, size: float, unit: int = 2): # unit=2 means SP
self._text_size = size
def getTextSize(self) -> float:
return self._text_size
def setTextColor(self, color: int):
self._text_color = color
def getCurrentTextColor(self) -> int:
return self._text_color
def setHintTextColor(self, color: int):
self._hint_text_color = color
def getHintTextColor(self) -> int:
return self._hint_text_color
def setLinkTextColor(self, color: int):
self._link_text_color = color
def getLinkTextColor(self) -> int:
return self._link_text_color
def setHighlightColor(self, color: int):
self._highlight_color = color
def getHighlightColor(self) -> int:
return self._highlight_color
# ─── gravity ──────────────────────────────────────────────
def setGravity(self, gravity: int):
self._gravity = gravity
def getGravity(self) -> int:
return self._gravity
# ─── typeface ──────────────────────────────────────────────
def setTypeface(self, typeface: Any, style: int = 0):
self._typeface = typeface
def getTypeface(self) -> Any:
return self._typeface
# ─── hint ──────────────────────────────────────────────────
def setHint(self, hint: str):
self._hint = str(hint) if hint is not None else ""
def getHint(self) -> str:
return self._hint
# ─── input type ────────────────────────────────────────────
def setInputType(self, input_type: int):
self._input_type = input_type
def getInputType(self) -> int:
return self._input_type
# ─── lines ─────────────────────────────────────────────────
def setLines(self, lines: int):
self._lines = lines
def setMaxLines(self, max_lines: int):
self._max_lines = max_lines
def getMaxLines(self) -> int:
return self._max_lines
def setMinLines(self, min_lines: int):
self._min_lines = min_lines
def getMinLines(self) -> int:
return self._min_lines
def getLineCount(self) -> int:
return self._line_count
def setSingleLine(self, single_line: bool = True):
self._single_line = single_line
# ─── ellipsize ─────────────────────────────────────────────
def setEllipsize(self, where: Optional[str]):
self._ellipsize = where
def getEllipsize(self) -> Optional[str]:
return self._ellipsize
# ─── movement method ──────────────────────────────────────
def setMovementMethod(self, movement: Any):
self._movement_method = movement
def getMovementMethod(self) -> Any:
return self._movement_method
# ─── auto link ─────────────────────────────────────────────
def setAutoLinkMask(self, mask: int):
self._auto_link_mask = mask
def getAutoLinkMask(self) -> int:
return self._auto_link_mask
# ─── transformation ───────────────────────────────────────
def setTransformationMethod(self, method: Optional[Callable]):
self._transformation_method = method
def getTransformationMethod(self) -> Optional[Callable]:
return self._transformation_method
# ─── shadow ────────────────────────────────────────────────
def setShadowLayer(self, radius: float, dx: float, dy: float, color: int):
self._shadow_radius = radius
self._shadow_dx = dx
self._shadow_dy = dy
self._shadow_color = color
# ─── line spacing ─────────────────────────────────────────
def setLineSpacing(self, extra: float, multiplier: float):
self._line_spacing_extra = extra
self._line_spacing_mult = multiplier
# ─── compound drawables ───────────────────────────────────
def setCompoundDrawables(self, left: Any, top: Any, right: Any, bottom: Any):
self._compound_drawables = [left, top, right, bottom]
def setCompoundDrawablesWithIntrinsicBounds(self, left: Any, top: Any,
right: Any, bottom: Any):
self._compound_drawables = [left, top, right, bottom]
def setCompoundDrawablePadding(self, pad: int):
self._compound_drawable_padding = pad
def getCompoundDrawablePadding(self) -> int:
return self._compound_drawable_padding
def getCompoundDrawables(self) -> List[Any]:
return list(self._compound_drawables)
# ─── editor action ────────────────────────────────────────
def setOnEditorActionListener(self, listener: Optional[Callable]):
self._on_editor_action_listener = listener
# ─── key listener ─────────────────────────────────────────
def setKeyListener(self, key_listener: Any):
self._key_listener = key_listener
def getKeyListener(self) -> Any:
return self._key_listener
# ─── filters ───────────────────────────────────────────────
def setFilters(self, filters: List[Any]):
self._filters = list(filters)
def getFilters(self) -> List[Any]:
return list(self._filters)
# ─── misc ──────────────────────────────────────────────────
def setSelectAllOnFocus(self, select_all_on_focus: bool):
self._select_all_on_focus = select_all_on_focus
def setHorizontallyScrolling(self, horizontally_scrolling: bool):
self._horizontally_scrolling = horizontally_scrolling
def setIncludeFontPadding(self, include_font_padding: bool):
self._include_font_padding = include_font_padding
def setElegantTextHeight(self, elegant: bool):
self._elegant_text_height = elegant
def setLetterSpacing(self, letter_spacing: float):
self._letter_spacing = letter_spacing
def getLetterSpacing(self) -> float:
return self._letter_spacing
def setFontFeatureSettings(self, settings: str):
self._font_feature_settings = settings
def getFontFeatureSettings(self) -> str:
return self._font_feature_settings
def getPaint(self) -> Any:
return self._paint
def getLayout(self) -> Any:
return self._layout
def setLinksClickable(self, links_clickable: bool):
self._links_clickable = links_clickable
def getLinksClickable(self) -> bool:
return self._links_clickable
def setCursorVisible(self, visible: bool):
self._cursor_visible = visible
def isCursorVisible(self) -> bool:
return self._cursor_visible
def setTextCursorDrawable(self, drawable: Any):
pass
def setFreezesText(self, freezes_text: bool):
pass
def getFreezesText(self) -> bool:
return False
def setEditableFactory(self, factory: Any):
pass
def setSpannableFactory(self, factory: Any):
pass
def getTextSize(self) -> float:
return self._text_size
def setBackgroundColor(self, color: int):
super().setBackgroundColor(color)
def setPadding(self, left: int, top: int, right: int, bottom: int):
super().setPadding(left, top, right, bottom)
def onMeasure(self, width_measure_spec: int, height_measure_spec: int):
from .view import MeasureSpec
w = MeasureSpec.getSize(width_measure_spec)
h = MeasureSpec.getSize(height_measure_spec)
if MeasureSpec.getMode(width_measure_spec) == MeasureSpec.AT_MOST:
text_len = len(self._text) * 4
w = min(w, max(100, text_len))
if MeasureSpec.getMode(height_measure_spec) == MeasureSpec.AT_MOST:
text_lines = self._line_count
h = min(h, max(20, text_lines * int(self._text_size) + 10))
self.setMeasuredDimension(w, h)
def onDraw(self, canvas: Any):
pass
# ═══════════════════════════════════════════════════════════════
# EditText
# ═══════════════════════════════════════════════════════════════
class EditText(TextView):
"""EditText — 可编辑文本"""
def __init__(self, context=None):
super().__init__(context)
self._editable = True
self._selection_start = 0
self._selection_end = 0
def setSelection(self, start: int, stop: Optional[int] = None):
if stop is None:
self._selection_start = start
self._selection_end = start
else:
self._selection_start = start
self._selection_end = stop
def getSelectionStart(self) -> int:
return self._selection_start
def getSelectionEnd(self) -> int:
return self._selection_end
def selectAll(self):
self._selection_start = 0
self._selection_end = len(self._text)
def extendSelection(self, index: int):
self._selection_end = index
def setText(self, text: str, buffer_type: int = TextView.BUFFER_TYPE_EDITABLE):
super().setText(text, buffer_type)
def getEditableText(self):
return self._text
# ═══════════════════════════════════════════════════════════════
# Button
# ═══════════════════════════════════════════════════════════════
class Button(TextView):
"""Button — 按钮"""
def __init__(self, context=None):
super().__init__(context)
self._clickable = True
self._focusable = True
def onTouchEvent(self, event: MotionEvent) -> bool:
if event.getAction() == MotionEvent.ACTION_UP:
self.performClick()
return super().onTouchEvent(event)
class CheckBox(Button):
"""CheckBox — 复选框"""
def __init__(self, context=None):
super().__init__(context)
self._checked = False
def setChecked(self, checked: bool):
self._checked = checked
def isChecked(self) -> bool:
return self._checked
def toggle(self):
self._checked = not self._checked
def performClick(self) -> bool:
self.toggle()
return super().performClick()
class RadioButton(Button):
"""RadioButton — 单选按钮"""
def __init__(self, context=None):
super().__init__(context)
self._checked = False
def setChecked(self, checked: bool):
self._checked = checked
def isChecked(self) -> bool:
return self._checked
def toggle(self):
self._checked = not self._checked
def performClick(self) -> bool:
self.toggle()
return super().performClick()
class ToggleButton(Button):
"""ToggleButton — 开关按钮"""
def __init__(self, context=None):
super().__init__(context)
self._checked = False
self._text_on = "ON"
self._text_off = "OFF"
self._disabled_alpha = 0.5
def setChecked(self, checked: bool):
self._checked = checked
if self._checked:
self.setText(self._text_on)
else:
self.setText(self._text_off)
def isChecked(self) -> bool:
return self._checked
def toggle(self):
self.setChecked(not self._checked)
def setTextOn(self, text_on: str):
self._text_on = text_on
if self._checked:
self.setText(text_on)
def getTextOn(self) -> str:
return self._text_on
def setTextOff(self, text_off: str):
self._text_off = text_off
if not self._checked:
self.setText(text_off)
def getTextOff(self) -> str:
return self._text_off
def performClick(self) -> bool:
self.toggle()
return super().performClick()
class Switch(Button):
"""Switch — 开关组件"""
def __init__(self, context=None):
super().__init__(context)
self._checked = False
self._show_text = True
self._split_track = False
self._thumb_text_padding: int = 0
self._switch_min_width: int = 0
self._switch_padding: int = 0
self._thumb_drawable: Any = None
self._track_drawable: Any = None
self._text_on = "ON"
self._text_off = "OFF"
self._thumb_width = 40
self._switch_width = 100
def isChecked(self) -> bool:
return self._checked
def setChecked(self, checked: bool):
self._checked = checked
def toggle(self):
self._checked = not self._checked
def setShowText(self, show_text: bool):
self._show_text = show_text
def getShowText(self) -> bool:
return self._show_text
def setSplitTrack(self, split_track: bool):
self._split_track = split_track
def getSplitTrack(self) -> bool:
return self._split_track
def setThumbTextPadding(self, padding: int):
self._thumb_text_padding = padding
def getThumbTextPadding(self) -> int:
return self._thumb_text_padding
def setSwitchMinWidth(self, min_width: int):
self._switch_min_width = min_width
def getSwitchMinWidth(self) -> int:
return self._switch_min_width
def setSwitchPadding(self, padding: int):
self._switch_padding = padding
def getSwitchPadding(self) -> int:
return self._switch_padding
def getThumbDrawable(self) -> Any:
return self._thumb_drawable
def setThumbDrawable(self, drawable: Any):
self._thumb_drawable = drawable
def getTrackDrawable(self) -> Any:
return self._track_drawable
def setTrackDrawable(self, drawable: Any):
self._track_drawable = drawable
def setTextOn(self, text_on: str):
self._text_on = text_on
def getTextOn(self) -> str:
return self._text_on
def setTextOff(self, text_off: str):
self._text_off = text_off
def getTextOff(self) -> str:
return self._text_off
def performClick(self) -> bool:
self.toggle()
return super().performClick()
def onMeasure(self, width_measure_spec: int, height_measure_spec: int):
from .view import MeasureSpec
w = MeasureSpec.getSize(width_measure_spec)
h = MeasureSpec.getSize(height_measure_spec)
self.setMeasuredDimension(w, max(30, h))
# ═══════════════════════════════════════════════════════════════
# ImageView
# ═══════════════════════════════════════════════════════════════
class ImageView(View):
"""ImageView — 图片视图"""
# ScaleType常量
ScaleType = type("ScaleType", (), {
"MATRIX": 0,
"FIT_XY": 1,
"FIT_START": 2,
"FIT_CENTER": 3,
"FIT_END": 4,
"CENTER": 5,
"CENTER_CROP": 6,
"CENTER_INSIDE": 7,
})
def __init__(self, context=None):
super().__init__(context)
self._drawable: Any = None
self._image_resource: int = 0
self._uri: Any = None
self._scale_type: int = ImageView.ScaleType.FIT_CENTER
self._max_width: int = 0
self._max_height: int = 0
self._adjust_view_bounds: bool = False
self._crop_to_padding: bool = False
self._baseline: int = 0
self._baseline_align_bottom: bool = False
self._image_matrix: Any = None
self._image_bitmap: Any = None
def setImageDrawable(self, drawable: Any):
self._drawable = drawable
def getDrawable(self) -> Any:
return self._drawable
def setImageResource(self, res_id: int):
self._image_resource = res_id
def setImageURI(self, uri: Any):
self._uri = uri
def setImageBitmap(self, bitmap: Any):
self._image_bitmap = bitmap
def getImageBitmap(self) -> Any:
return self._image_bitmap
def setScaleType(self, scale_type: int):
self._scale_type = scale_type
def getScaleType(self) -> int:
return self._scale_type
def setMaxWidth(self, max_width: int):
self._max_width = max_width
def setMaxHeight(self, max_height: int):
self._max_height = max_height
def setAdjustViewBounds(self, adjust: bool):
self._adjust_view_bounds = adjust
def getAdjustViewBounds(self) -> bool:
return self._adjust_view_bounds
def setCropToPadding(self, crop: bool):
self._crop_to_padding = crop
def getCropToPadding(self) -> bool:
return self._crop_to_padding
def setBaseline(self, baseline: int):
self._baseline = baseline
def getBaseline(self) -> int:
return self._baseline
def setBaselineAlignBottom(self, aligned: bool):
self._baseline_align_bottom = aligned
def getBaselineAlignBottom(self) -> bool:
return self._baseline_align_bottom
def setImageMatrix(self, matrix: Any):
self._image_matrix = matrix
def getImageMatrix(self) -> Any:
return self._image_matrix
def invalidateDrawable(self, dr: Any):
pass
def onMeasure(self, width_measure_spec: int, height_measure_spec: int):
from .view import MeasureSpec
w = MeasureSpec.getSize(width_measure_spec)
h = MeasureSpec.getSize(height_measure_spec)
if w == 0:
w = 100
if h == 0:
h = 100
self.setMeasuredDimension(w, h)
class ImageButton(ImageView):
"""ImageButton — 图片按钮"""
def __init__(self, context=None):
super().__init__(context)
self._clickable = True
self._focusable = True
def onTouchEvent(self, event: MotionEvent) -> bool:
if event.getAction() == MotionEvent.ACTION_UP:
self.performClick()
return super().onTouchEvent(event)
# ═══════════════════════════════════════════════════════════════
# ProgressBar
# ═══════════════════════════════════════════════════════════════
class ProgressBar(View):
"""ProgressBar — 进度条"""
def __init__(self, context=None, attrs: Any = None, def_style_attr: int = 0,
def_style_res: int = 0):
super().__init__(context)
self._progress: int = 0
self._max: int = 100
self._indeterminate: bool = False
self._indeterminate_drawable: Any = None
self._progress_drawable: Any = None
self._secondary_progress: int = 0
self._min: int = 0
self._interpolator: Any = None
self._animation: Any = None
def setProgress(self, progress: int):
self._progress = max(self._min, min(self._max, progress))
def getProgress(self) -> int:
return self._progress
def setMax(self, max_val: int):
self._max = max_val
if self._progress > self._max:
self._progress = self._max
def getMax(self) -> int:
return self._max
def setMin(self, min_val: int):
self._min = min_val
def getMin(self) -> int:
return self._min
def incrementProgressBy(self, diff: int):
self.setProgress(self._progress + diff)
def setIndeterminate(self, indeterminate: bool):
self._indeterminate = indeterminate
def isIndeterminate(self) -> bool:
return self._indeterminate
def getIndeterminateDrawable(self) -> Any:
return self._indeterminate_drawable
def setIndeterminateDrawable(self, drawable: Any):
self._indeterminate_drawable = drawable
def setProgressDrawable(self, drawable: Any):
self._progress_drawable = drawable
def getProgressDrawable(self) -> Any:
return self._progress_drawable
def setSecondaryProgress(self, secondary_progress: int):
self._secondary_progress = max(self._min, min(self._max, secondary_progress))
def getSecondaryProgress(self) -> int:
return self._secondary_progress
def setInterpolator(self, interpolator: Any):
self._interpolator = interpolator
def getInterpolator(self) -> Any:
return self._interpolator
def setAnimation(self, animation: Any):
self._animation = animation
def onMeasure(self, width_measure_spec: int, height_measure_spec: int):
from .view import MeasureSpec
w = MeasureSpec.getSize(width_measure_spec)
h = MeasureSpec.getSize(height_measure_spec)
if h == 0:
h = 20
self.setMeasuredDimension(w, h)
class SeekBar(ProgressBar):
"""SeekBar — 可拖动进度条"""
def __init__(self, context=None, attrs: Any = None,
def_style_attr: int = 0, def_style_res: int = 0):
super().__init__(context, attrs, def_style_attr, def_style_res)
self._on_seek_bar_change_listener: Optional[Callable] = None
class OnSeekBarChangeListener:
def onProgressChanged(self, seek_bar: "SeekBar", progress: int, from_user: bool):
pass
def onStartTrackingTouch(self, seek_bar: "SeekBar"):
pass
def onStopTrackingTouch(self, seek_bar: "SeekBar"):
pass
def setOnSeekBarChangeListener(self, listener: Optional[OnSeekBarChangeListener]):
self._on_seek_bar_change_listener = listener
def getOnSeekBarChangeListener(self) -> Optional[OnSeekBarChangeListener]:
return self._on_seek_bar_change_listener
def setThumb(self, thumb: Any):
pass
def getThumb(self) -> Any:
return None
def setThumbOffset(self, thumb_offset: int):
pass
def getThumbOffset(self) -> int:
return 0
def setKeyProgressIncrement(self, increment: int):
pass
def getKeyProgressIncrement(self) -> int:
return 1
class RatingBar(ProgressBar):
"""RatingBar — 评分条"""
def __init__(self, context=None, attrs: Any = None,
def_style_attr: int = 0, def_style_res: int = 0):
super().__init__(context, attrs, def_style_attr, def_style_res)
self._num_stars: int = 5
self._rating: float = 0.0
self._step_size: float = 0.5
self._is_indicator: bool = False
self._on_rating_bar_change_listener: Optional[Callable] = None
class OnRatingBarChangeListener:
def onRatingChanged(self, rating_bar: "RatingBar", rating: float, from_user: bool):
pass
def setOnRatingBarChangeListener(self, listener: Optional[OnRatingBarChangeListener]):
self._on_rating_bar_change_listener = listener
def getOnRatingBarChangeListener(self) -> Optional[OnRatingBarChangeListener]:
return self._on_rating_bar_change_listener
def setNumStars(self, num_stars: int):
self._num_stars = max(1, num_stars)
def getNumStars(self) -> int:
return self._num_stars
def setRating(self, rating: float):
self._rating = max(0.0, min(float(self._num_stars), rating))
def getRating(self) -> float:
return self._rating
def setStepSize(self, step_size: float):
self._step_size = max(0.1, step_size)
def getStepSize(self) -> float:
return self._step_size
def setIsIndicator(self, is_indicator: bool):
self._is_indicator = is_indicator
def isIndicator(self) -> bool:
return self._is_indicator
# ═══════════════════════════════════════════════════════════════
# AdapterView
# ═══════════════════════════════════════════════════════════════
class AdapterView(ViewGroup):
"""AdapterView — 适配器视图基类"""
CHOICE_MODE_NONE = 0
CHOICE_MODE_SINGLE = 1
CHOICE_MODE_MULTIPLE = 2
CHOICE_MODE_MULTIPLE_MODAL = 3
INVALID_POSITION = -1
INVALID_ROW_ID = -9223372036854775808 # Long.MIN_VALUE
def __init__(self, context=None):
super().__init__(context)
self._adapter: Any = None
self._on_item_click_listener: Optional[Callable] = None
self._on_item_long_click_listener: Optional[Callable] = None
self._on_item_selected_listener: Optional[Callable] = None
self._choice_mode = AdapterView.CHOICE_MODE_NONE
self._checked_item_positions: Dict[int, bool] = {}
self._checked_id_positions: Dict[int, bool] = {}
self._selected_position = AdapterView.INVALID_POSITION
self._empty_view: Optional[View] = None
self._selection_notifier: Any = None
def setAdapter(self, adapter: Any):
self._adapter = adapter
def getAdapter(self) -> Any:
return self._adapter
def setOnItemClickListener(self, listener: Optional[Callable]):
self._on_item_click_listener = listener
def getOnItemClickListener(self) -> Optional[Callable]:
return self._on_item_click_listener
def setOnItemLongClickListener(self, listener: Optional[Callable]):
self._on_item_long_click_listener = listener
def getOnItemLongClickListener(self) -> Optional[Callable]:
return self._on_item_long_click_listener
def setOnItemSelectedListener(self, listener: Optional[Callable]):
self._on_item_selected_listener = listener
def getOnItemSelectedListener(self) -> Optional[Callable]:
return self._on_item_selected_listener
def getSelectedItemPosition(self) -> int:
return self._selected_position
def getSelectedItemId(self) -> int:
return 0
def getSelectedItem(self) -> Any:
if self._adapter is not None and 0 <= self._selected_position < self._adapter.getCount():
return self._adapter.getItem(self._selected_position)
return None
def setSelection(self, position: int):
self._selected_position = position
def setEmptyView(self, empty_view: View):
self._empty_view = empty_view
def getEmptyView(self) -> Optional[View]:
return self._empty_view
def setChoiceMode(self, choice_mode: int):
self._choice_mode = choice_mode
def getChoiceMode(self) -> int:
return self._choice_mode
def setItemChecked(self, position: int, checked: bool):
if self._choice_mode == AdapterView.CHOICE_MODE_SINGLE:
self._checked_item_positions.clear()
self._checked_item_positions[position] = checked
elif self._choice_mode == AdapterView.CHOICE_MODE_MULTIPLE:
self._checked_item_positions[position] = checked
def isItemChecked(self, position: int) -> bool:
return self._checked_item_positions.get(position, False)
def getCheckedItemPositions(self) -> Dict[int, bool]:
return dict(self._checked_item_positions)
def getCheckedItemCount(self) -> int:
return len(self._checked_item_positions)
def getCheckedItemIds(self) -> List[int]:
return list(self._checked_id_positions.keys())
def performItemClick(self, view: View, position: int, id_: int) -> bool:
if self._on_item_click_listener:
self._on_item_click_listener(self, view, position, id_)
return True
return False
# ═══════════════════════════════════════════════════════════════
# ListView
# ═══════════════════════════════════════════════════════════════
class ListView(AdapterView):
"""ListView — 列表视图"""
CHOICE_MODE_NONE = 0
CHOICE_MODE_SINGLE = 1
CHOICE_MODE_MULTIPLE = 2
CHOICE_MODE_MULTIPLE_MODAL = 3
TRANSCRIPT_MODE_DISABLED = 0
TRANSCRIPT_MODE_NORMAL = 1
TRANSCRIPT_MODE_ALWAYS_SCROLL = 2
def __init__(self, context=None):
super().__init__(context)
self._divider: Any = None
self._divider_height: int = 0
self._footer_divider: Any = None
self._header_divider: Any = None
self._footer_views: List[View] = []
self._header_views: List[View] = []
self._transcript_mode = ListView.TRANSCRIPT_MODE_DISABLED
self._items_can_focus = True
self._smooth_scroll_position = 0
self._stack_from_bottom = False
self._selection_division = 0
def setAdapter(self, adapter: Any):
super().setAdapter(adapter)
def getAdapter(self) -> Any:
return super().getAdapter()
def setSelection(self, position: int):
super().setSelection(position)
def smoothScrollToPosition(self, position: int):
self._smooth_scroll_position = position
def setDivider(self, divider: Any):
self._divider = divider
def setDividerHeight(self, height: int):
self._divider_height = height
def getDivider(self) -> Any:
return self._divider
def getDividerHeight(self) -> int:
return self._divider_height
def addFooterView(self, v: View, data: Any = None, is_selectable: bool = True):
self._footer_views.append(v)
def removeFooterView(self, v: View) -> bool:
if v in self._footer_views:
self._footer_views.remove(v)
return True
return False
def addHeaderView(self, v: View, data: Any = None, is_selectable: bool = True):
self._header_views.append(v)
def removeHeaderView(self, v: View) -> bool:
if v in self._header_views:
self._header_views.remove(v)
return True
return False
def getHeaderViewsCount(self) -> int:
return len(self._header_views)
def getFooterViewsCount(self) -> int:
return len(self._footer_views)
def setTranscriptMode(self, mode: int):
self._transcript_mode = mode
def getTranscriptMode(self) -> int:
return self._transcript_mode
def setItemsCanFocus(self, items_can_focus: bool):
self._items_can_focus = items_can_focus
def getItemsCanFocus(self) -> bool:
return self._items_can_focus
def setStackFromBottom(self, stack_from_bottom: bool):
self._stack_from_bottom = stack_from_bottom
def isStackFromBottom(self) -> bool:
return self._stack_from_bottom
def getCheckItemIds(self) -> List[int]:
return list(self._checked_id_positions.keys())
class GridView(AdapterView):
"""GridView — 网格视图"""
def __init__(self, context=None):
super().__init__(context)
self._num_columns: int = 1
self._column_width: int = 0
self._horizontal_spacing: int = 0
self._vertical_spacing: int = 0
self._stretch_mode: int = 0 # NO_STRETCH
self._gravity = Gravity.LEFT
STRETCH_NO_STRETCH = 0
STRETCH_STRETCH_COLUMN_WIDTH = 1
STRETCH_STRETCH_SPACING = 2
STRETCH_STRETCH_SPACING_UNIFORM = 3
STRETCH_STRETCH_COLUMN_WIDTH_UNIFORM = 4
def setNumColumns(self, num_columns: int):
self._num_columns = max(1, num_columns)
def getNumColumns(self) -> int:
return self._num_columns
def setColumnWidth(self, column_width: int):
self._column_width = column_width
def getColumnWidth(self) -> int:
return self._column_width
def setHorizontalSpacing(self, horizontal_spacing: int):
self._horizontal_spacing = horizontal_spacing
def getHorizontalSpacing(self) -> int:
return self._horizontal_spacing
def setVerticalSpacing(self, vertical_spacing: int):
self._vertical_spacing = vertical_spacing
def getVerticalSpacing(self) -> int:
return self._vertical_spacing
def setStretchMode(self, stretch_mode: int):
self._stretch_mode = stretch_mode
def getStretchMode(self) -> int:
return self._stretch_mode
def setGravity(self, gravity: int):
self._gravity = gravity
def getGravity(self) -> int:
return self._gravity
def setAdapter(self, adapter: Any):
super().setAdapter(AdapterView)
# ═══════════════════════════════════════════════════════════════
# RecyclerView
# ═══════════════════════════════════════════════════════════════
class RecyclerView(ViewGroup):
"""RecyclerView — 可回收视图列表"""
HORIZONTAL = 0
VERTICAL = 1
NO_POSITION = -1
INVALID_TYPE = -1
TOUCH_SLOP_DEFAULT = 0
TOUCH_SLOP_PAGING = 1
def __init__(self, context=None):
super().__init__(context)
self._adapter: Optional["RecyclerView.Adapter"] = None
self._layout_manager: Optional["RecyclerView.LayoutManager"] = None
self._item_decorations: List[Any] = []
self._recycler: Any = None
self._child_attached_listener: Optional[Callable] = None
self._child_detached_listener: Optional[Callable] = None
self._item_animator: Any = None
self._on_scroll_listener: Optional[Callable] = None
self._nested_scrolling_enabled = True
self._scroll_state = RecyclerView.SCROLL_STATE_IDLE
self._layout_frozen = False
self._suppress_layout = False
self._clip_to_padding = True
SCROLL_STATE_IDLE = 0
SCROLL_STATE_DRAGGING = 1
SCROLL_STATE_SETTLING = 2
# ─── Adapter ─────────────────────────────────────────────
class Adapter(Stub):
"""RecyclerView.Adapter — 适配器"""
def __init__(self):
super().__init__()
self._observable: Any = None
self._has_stable_ids = False
self._state_restoration_policy = 0
def onCreateViewHolder(self, parent: "RecyclerView", view_type: int) -> "RecyclerView.ViewHolder":
raise NotImplementedError
def onBindViewHolder(self, holder: "RecyclerView.ViewHolder", position: int):
raise NotImplementedError
def getItemCount(self) -> int:
return 0
def getItemViewType(self, position: int) -> int:
return 0
def getItemId(self, position: int) -> int:
return position
def hasStableIds(self) -> bool:
return self._has_stable_ids
def setHasStableIds(self, has_stable_ids: bool):
self._has_stable_ids = has_stable_ids
def onAttachedToRecyclerView(self, recycler_view: "RecyclerView"):
pass
def onDetachedFromRecyclerView(self, recycler_view: "RecyclerView"):
pass
def onViewAttachedToWindow(self, holder: "RecyclerView.ViewHolder"):
pass
def onViewDetachedFromWindow(self, holder: "RecyclerView.ViewHolder"):
pass
def onViewRecycled(self, holder: "RecyclerView.ViewHolder"):
pass
def onFailedToRecycleView(self, holder: "RecyclerView.ViewHolder") -> bool:
return False
def notifyDataSetChanged(self):
for observer in getattr(self, '_observers', []):
observer.onChanged()
def notifyItemChanged(self, position, payload=None):
for observer in getattr(self, '_observers', []):
observer.onItemRangeChanged(position, 1, payload)
def notifyItemInserted(self, position):
for observer in getattr(self, '_observers', []):
observer.onItemRangeInserted(position, 1)
def notifyItemRemoved(self, position):
for observer in getattr(self, '_observers', []):
observer.onItemRangeRemoved(position, 1)
def notifyItemMoved(self, fromPosition, toPosition):
for observer in getattr(self, '_observers', []):
observer.onItemRangeMoved(fromPosition, toPosition, 1)
def notifyItemRangeChanged(self, positionStart, itemCount, payload=None):
for observer in getattr(self, '_observers', []):
observer.onItemRangeChanged(positionStart, itemCount, payload)
def notifyItemRangeInserted(self, positionStart, itemCount):
for observer in getattr(self, '_observers', []):
observer.onItemRangeInserted(positionStart, itemCount)
def notifyItemRangeRemoved(self, positionStart, itemCount):
for observer in getattr(self, '_observers', []):
observer.onItemRangeRemoved(positionStart, itemCount)
def registerAdapterDataObserver(self, observer):
if not hasattr(self, '_observers'):
self._observers = []
self._observers.append(observer)
def unregisterAdapterDataObserver(self, observer):
if hasattr(self, '_observers'):
try:
self._observers.remove(observer)
except ValueError:
pass
def getStateRestorationPolicy(self) -> int:
return 0
# ─── ViewHolder ───────────────────────────────────────────
class ViewHolder(Stub):
"""RecyclerView.ViewHolder — 视图持有者"""
FLAG_BOUND = 1
FLAG_NOT_RECYCLABLE = 2
FLAG_RETURNED_FROM_SCRAP = 4
FLAG_CHANGED = 8
FLAG_IGNORE = 16
FLAG_TMP_DETACHED = 32
FLAG_UPDATE = 64
FLAG_ADAPTER_POSITION_UNKNOWN = 128
FLAG_ADAPTER_FULLUPDATE = 256
FLAG_MOVED = 512
FLAG_APPEARED_IN_PRE_LAYOUT = 1024
def __init__(self, item_view: View):
super().__init__()
self.itemView = item_view
self._position = RecyclerView.NO_POSITION
self._old_position = RecyclerView.NO_POSITION
self._item_id = RecyclerView.NO_ID
self._item_view_type = RecyclerView.INVALID_TYPE
self._flags = 0
self._pre_layout_position = RecyclerView.NO_POSITION
self._is_recyclable = True
self._scrap_container: Optional[RecyclerView] = None
self._shadowed_holder: Optional["RecyclerView.ViewHolder"] = None
self._shadowing_holder: Optional["RecyclerView.ViewHolder"] = None
NO_ID = -1
def getAdapterPosition(self) -> int:
return self._position
def getLayoutPosition(self) -> int:
return self._position
def getOldPosition(self) -> int:
return self._old_position
def getItemId(self) -> int:
return self._item_id
def getItemViewType(self) -> int:
return self._item_view_type
def setIsRecyclable(self, recyclable: bool):
self._is_recyclable = recyclable
def isRecyclable(self) -> bool:
return self._is_recyclable
def getBindingAdapterPosition(self) -> int:
return self._position
# ─── LayoutManager ────────────────────────────────────────
class LayoutManager(Stub):
"""RecyclerView.LayoutManager — 布局管理器"""
def __init__(self):
super().__init__()
self._recycler_view: Optional["RecyclerView"] = None
self._child_count = 0
self._width = 0
self._height = 0
self._auto_measure = True
self._item_prefetch_enabled = True
def onLayoutChildren(self, recycler: Any, state: Any):
pass
def getChildCount(self) -> int:
return self._child_count
def getPosition(self, view: View) -> int:
if isinstance(view, View):
return 0
return RecyclerView.NO_POSITION
def scrollToPosition(self, position: int):
pass
def smoothScrollToPosition(self, recycler_view: "RecyclerView", state: Any, position: int):
pass
def onAdapterChanged(self, old_adapter: Any, new_adapter: Any):
pass
def generateDefaultLayoutParams(self) -> ViewGroup.LayoutParams:
return ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
def getWidth(self) -> int:
return self._width
def getHeight(self) -> int:
return self._height
def setAutoMeasureEnabled(self, enabled: bool):
self._auto_measure = enabled
def isAutoMeasureEnabled(self) -> bool:
return self._auto_measure
def requestLayout(self):
pass
def requestSimpleAnimationsInNextLayout(self):
pass
def assertNotInLayoutOrScroll(self, tag: str = ""):
pass
def getPaddingLeft(self) -> int:
return 0
def getPaddingTop(self) -> int:
return 0
def getPaddingRight(self) -> int:
return 0
def getPaddingBottom(self) -> int:
return 0
# ─── ItemDecoration ──────────────────────────────────────
class ItemDecoration(Stub):
"""RecyclerView.ItemDecoration — 条目装饰"""
def onDraw(self, c: Any, parent: "RecyclerView", state: Any):
pass
def onDrawOver(self, c: Any, parent: "RecyclerView", state: Any):
pass
def getItemOffsets(self, out_rect: Any, view: View, parent: "RecyclerView", state: Any):
if hasattr(out_rect, "set"):
out_rect.set(0, 0, 0, 0)
# ─── RecycledViewPool ─────────────────────────────────────
class RecycledViewPool(Stub):
"""RecyclerView.RecycledViewPool — 回收视图池"""
def __init__(self):
super().__init__()
self._max_recycled_views = 5
def getRecycledView(self, view_type: int) -> Optional["RecyclerView.ViewHolder"]:
return None
def putRecycledView(self, scrap: "RecyclerView.ViewHolder"):
pass
def setMaxRecycledViews(self, view_type: int, max_count: int):
self._max_recycled_views = max_count
def clear(self):
pass
# ─── Recycler ─────────────────────────────────────────────
class Recycler(Stub):
"""RecyclerView.Recycler — 回收器"""
def __init__(self):
super().__init__()
self._view_cache: Dict[int, List["RecyclerView.ViewHolder"]] = {}
def getViewForPosition(self, position: int) -> View:
raise AndroidOnlyError("RecyclerView.Recycler.getViewForPosition")
def recycleView(self, view: View):
pass
def getRecycledViewPool(self) -> "RecyclerView.RecycledViewPool":
return RecyclerView.RecycledViewPool()
def setViewCacheSize(self, view_count: int):
pass
def clear(self):
self._view_cache.clear()
# ─── RecyclerView Instance Methods ───────────────────────
def setAdapter(self, adapter: Optional["RecyclerView.Adapter"]):
self._adapter = adapter
def getAdapter(self) -> Optional["RecyclerView.Adapter"]:
return self._adapter
def setLayoutManager(self, layout_manager: Optional["RecyclerView.LayoutManager"]):
self._layout_manager = layout_manager
if layout_manager:
layout_manager._recycler_view = self
def getLayoutManager(self) -> Optional["RecyclerView.LayoutManager"]:
return self._layout_manager
def addItemDecoration(self, decoration: "RecyclerView.ItemDecoration", index: int = -1):
if index < 0 or index >= len(self._item_decorations):
self._item_decorations.append(decoration)
else:
self._item_decorations.insert(index, decoration)
def removeItemDecoration(self, decoration: "RecyclerView.ItemDecoration"):
if decoration in self._item_decorations:
self._item_decorations.remove(decoration)
def getItemDecorationAt(self, index: int) -> "RecyclerView.ItemDecoration":
return self._item_decorations[index]
def getItemDecorationCount(self) -> int:
return len(self._item_decorations)
def swapAdapter(self, adapter: Optional["RecyclerView.Adapter"], remove_and_recycle: bool):
self._adapter = adapter
def getRecycledViewPool(self) -> "RecyclerView.RecycledViewPool":
return RecyclerView.RecycledViewPool()
def setRecycledViewPool(self, pool: "RecyclerView.RecycledViewPool"):
pass
def getRecycler(self) -> "RecyclerView.Recycler":
return RecyclerView.Recycler()
def setItemAnimator(self, animator: Any):
self._item_animator = animator
def getItemAnimator(self) -> Any:
return self._item_animator
def setOnScrollListener(self, listener: Optional[Callable]):
self._on_scroll_listener = listener
def addOnScrollListener(self, listener: Callable):
pass
def removeOnScrollListener(self, listener: Callable):
pass
def clearOnScrollListeners(self):
pass
def addOnChildAttachStateChangeListener(self, listener: Callable):
self._child_attached_listener = listener
def removeOnChildAttachStateChangeListener(self, listener: Callable):
pass
def setHasFixedSize(self, has_fixed_size: bool):
pass
def hasFixedSize(self) -> bool:
return False
def setNestedScrollingEnabled(self, enabled: bool):
self._nested_scrolling_enabled = enabled
def isNestedScrollingEnabled(self) -> bool:
return self._nested_scrolling_enabled
def startNestedScroll(self, axes: int) -> bool:
return False
def stopNestedScroll(self):
pass
def hasNestedScrollingParent(self) -> bool:
return False
def dispatchNestedPreScroll(self, dx: int, dy: int, consumed: List[int], offset: List[int]) -> bool:
return False
def dispatchNestedScroll(self, dx_consumed: int, dy_consumed: int,
dx_unconsumed: int, dy_unconsumed: int,
offset: List[int]) -> bool:
return False
def dispatchNestedPreFling(self, velocity_x: float, velocity_y: float) -> bool:
return False
def dispatchNestedFling(self, velocity_x: float, velocity_y: float, consumed: bool) -> bool:
return False
def stopScroll(self):
pass
def getScrollState(self) -> int:
return self._scroll_state
def setLayoutFrozen(self, frozen: bool):
self._layout_frozen = frozen
def isLayoutFrozen(self) -> bool:
return self._layout_frozen
def smoothScrollBy(self, dx: int, dy: int):
pass
def scrollBy(self, x: int, y: int):
pass
def computeHorizontalScrollOffset(self) -> int:
return 0
def computeHorizontalScrollRange(self) -> int:
return 0
def computeVerticalScrollOffset(self) -> int:
return 0
def computeVerticalScrollRange(self) -> int:
return 0
def findViewHolderForAdapterPosition(self, position: int) -> Optional["RecyclerView.ViewHolder"]:
return None
def findViewHolderForLayoutPosition(self, position: int) -> Optional["RecyclerView.ViewHolder"]:
return None
def findViewHolderForItemId(self, id_: int) -> Optional["RecyclerView.ViewHolder"]:
return None
NO_ID = -1
# 为 __all__ 导出创建别名
RecyclerViewAdapter = RecyclerView.Adapter
RecyclerViewViewHolder = RecyclerView.ViewHolder
RecyclerViewLayoutManager = RecyclerView.LayoutManager
class LinearLayoutManager(RecyclerView.LayoutManager):
"""LinearLayoutManager — 线性布局管理器"""
HORIZONTAL = 0
VERTICAL = 1
def __init__(self, context: Any = None, orientation: int = VERTICAL, reverse_layout: bool = False):
super().__init__()
self._orientation = orientation
self._reverse_layout = reverse_layout
self._stack_from_end = False
self._item_count = 0
def setOrientation(self, orientation: int):
self._orientation = orientation
def getOrientation(self) -> int:
return self._orientation
def setReverseLayout(self, reverse_layout: bool):
self._reverse_layout = reverse_layout
def getReverseLayout(self) -> bool:
return self._reverse_layout
def setStackFromEnd(self, stack_from_end: bool):
self._stack_from_end = stack_from_end
def getStackFromEnd(self) -> bool:
return self._stack_from_end
def generateDefaultLayoutParams(self) -> ViewGroup.LayoutParams:
return ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
def onLayoutChildren(self, recycler: Any, state: Any):
pass
class GridLayoutManager(LinearLayoutManager):
"""GridLayoutManager — 网格布局管理器"""
def __init__(self, context: Any = None, span_count: int = 1,
orientation: int = LinearLayoutManager.VERTICAL,
reverse_layout: bool = False):
super().__init__(context, orientation, reverse_layout)
self._span_count = max(1, span_count)
class SpanSizeLookup(Stub):
"""GridLayoutManager.SpanSizeLookup — 跨度大小查找"""
def __init__(self):
super().__init__()
self._span_index_cache = {}
def getSpanSize(self, position: int) -> int:
return 1
def getSpanIndex(self, position: int, span_count: int) -> int:
return position % span_count
def getSpanGroupIndex(self, position: int, span_count: int) -> int:
return position // span_count
def invalidateSpanIndexCache(self):
self._span_index_cache.clear()
def setSpanCount(self, span_count: int):
self._span_count = max(1, span_count)
def getSpanCount(self) -> int:
return self._span_count
def setSpanSizeLookup(self, span_size_lookup: SpanSizeLookup):
pass
def getSpanSizeLookup(self) -> SpanSizeLookup:
return GridLayoutManager.SpanSizeLookup()
class StaggeredGridLayoutManager(RecyclerView.LayoutManager):
"""StaggeredGridLayoutManager — 瀑布流布局管理器"""
HORIZONTAL = 0
VERTICAL = 1
GAP_HANDLING_NONE = 0
GAP_HANDLING_LAZY = 1
GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2
def __init__(self, span_count: int = 1, orientation: int = VERTICAL):
super().__init__()
self._span_count = max(1, span_count)
self._orientation = orientation
self._gap_strategy = StaggeredGridLayoutManager.GAP_HANDLING_LAZY
def setSpanCount(self, span_count: int):
self._span_count = max(1, span_count)
def getSpanCount(self) -> int:
return self._span_count
def setOrientation(self, orientation: int):
self._orientation = orientation
def getOrientation(self) -> int:
return self._orientation
def setGapStrategy(self, gap_strategy: int):
self._gap_strategy = gap_strategy
def getGapStrategy(self) -> int:
return self._gap_strategy
# ═══════════════════════════════════════════════════════════════
# LinearLayout
# ═══════════════════════════════════════════════════════════════
class LinearLayout(ViewGroup):
"""LinearLayout — 线性布局"""
HORIZONTAL = 0
VERTICAL = 1
SHOW_DIVIDER_NONE = 0
SHOW_DIVIDER_BEGINNING = 1
SHOW_DIVIDER_MIDDLE = 2
SHOW_DIVIDER_END = 4
def __init__(self, context=None):
super().__init__(context)
self._orientation = LinearLayout.VERTICAL
self._gravity = Gravity.NO_GRAVITY
self._weight_sum: float = 0.0
self._baseline_aligned = True
self._baseline_aligned_child_index = -1
self._divider: Any = None
self._divider_padding: int = 0
self._divider_show: int = LinearLayout.SHOW_DIVIDER_NONE
self._total_length: int = 0
def setOrientation(self, orientation: int):
self._orientation = orientation
def getOrientation(self) -> int:
return self._orientation
def setGravity(self, gravity: int):
self._gravity = gravity
def getGravity(self) -> int:
return self._gravity
def setWeightSum(self, weight_sum: float):
self._weight_sum = weight_sum
def getWeightSum(self) -> float:
return self._weight_sum
def setBaselineAligned(self, baseline_aligned: bool):
self._baseline_aligned = baseline_aligned
def isBaselineAligned(self) -> bool:
return self._baseline_aligned
def setDividerDrawable(self, divider: Any):
self._divider = divider
def setShowDividers(self, show_dividers: int):
self._divider_show = show_dividers
def getShowDividers(self) -> int:
return self._divider_show
def getDividerDrawable(self) -> Any:
return self._divider
def setDividerPadding(self, padding: int):
self._divider_padding = padding
def getDividerPadding(self) -> int:
return self._divider_padding
def getBaseline(self) -> int:
return -1
def onMeasure(self, width_measure_spec: int, height_measure_spec: int):
from .view import MeasureSpec
w = MeasureSpec.getSize(width_measure_spec)
h = MeasureSpec.getSize(height_measure_spec)
self.setMeasuredDimension(w, h)
def onLayout(self, changed: bool, l: int, t: int, r: int, b: int):
pass
class LayoutParams(ViewGroup.MarginLayoutParams):
"""LinearLayout.LayoutParams — 线性布局参数"""
def __init__(self, width: int = ViewGroup.LayoutParams.WRAP_CONTENT,
height: int = ViewGroup.LayoutParams.WRAP_CONTENT,
weight: float = 0.0):
super().__init__(width, height)
self.weight = weight
self.gravity = -1
def setLayoutWeight(self, weight: float):
self.weight = weight
# ═══════════════════════════════════════════════════════════════
# RelativeLayout
# ═══════════════════════════════════════════════════════════════
class RelativeLayout(ViewGroup):
"""RelativeLayout — 相对布局"""
TRUE = -1
FALSE = 0
# 规则常量
ALIGN_PARENT_LEFT = 0
ALIGN_PARENT_TOP = 1
ALIGN_PARENT_RIGHT = 2
ALIGN_PARENT_BOTTOM = 3
ALIGN_PARENT_START = 4
ALIGN_PARENT_END = 5
CENTER_IN_PARENT = 6
CENTER_HORIZONTAL = 7
CENTER_VERTICAL = 8
ABOVE = 9
BELOW = 10
LEFT_OF = 11
RIGHT_OF = 12
ALIGN_LEFT = 13
ALIGN_TOP = 14
ALIGN_RIGHT = 15
ALIGN_BOTTOM = 16
ALIGN_BASELINE = 17
ALIGN_WITH_PARENT_IF_MISSING = 18
START_OF = 19
END_OF = 20
ALIGN_START = 21
ALIGN_END = 22
def __init__(self, context=None):
super().__init__(context)
self._gravity = Gravity.NO_GRAVITY
self._ignore_gravity_of_view_ids: List[int] = []
self._has_left_weight = False
self._has_right_weight = False
self._has_top_weight = False
self._has_bottom_weight = False
def setGravity(self, gravity: int):
self._gravity = gravity
def getGravity(self) -> int:
return self._gravity
def setHorizontalGravity(self, horizontal_gravity: int):
pass
def setVerticalGravity(self, vertical_gravity: int):
pass
def onMeasure(self, width_measure_spec: int, height_measure_spec: int):
from .view import MeasureSpec
w = MeasureSpec.getSize(width_measure_spec)
h = MeasureSpec.getSize(height_measure_spec)
self.setMeasuredDimension(w, h)
def onLayout(self, changed: bool, l: int, t: int, r: int, b: int):
pass
class LayoutParams(ViewGroup.MarginLayoutParams):
"""RelativeLayout.LayoutParams — 相对布局参数"""
def __init__(self, width: int = ViewGroup.LayoutParams.WRAP_CONTENT,
height: int = ViewGroup.LayoutParams.WRAP_CONTENT):
super().__init__(width, height)
self._rules: Dict[int, int] = {}
self.align_with_parent = False
def addRule(self, verb: int, subject: int = -1):
self._rules[verb] = subject
def removeRule(self, verb: int):
self._rules.pop(verb, None)
def getRule(self, verb: int) -> int:
return self._rules.get(verb, 0)
def getRules(self) -> Dict[int, int]:
return dict(self._rules)
def addLayoutRules(self, layout_rules: int):
pass
def removeLayoutRule(self, layout_rule: int):
pass
# ═══════════════════════════════════════════════════════════════
# FrameLayout
# ═══════════════════════════════════════════════════════════════
class FrameLayout(ViewGroup):
"""FrameLayout — 帧布局"""
def __init__(self, context=None):
super().__init__(context)
self._foreground: Any = None
self._foreground_gravity = Gravity.FILL
self._measure_all_children = False
self._foreground_bounds_padding: int = 0
self._foreground_in_padding = False
def setForeground(self, drawable: Any):
self._foreground = drawable
def getForeground(self) -> Any:
return self._foreground
def setForegroundGravity(self, gravity: int):
self._foreground_gravity = gravity
def getForegroundGravity(self) -> int:
return self._foreground_gravity
def setMeasureAllChildren(self, measure_all: bool):
self._measure_all_children = measure_all
def getMeasureAllChildren(self) -> bool:
return self._measure_all_children
def setForegroundBoundsPadding(self, padding: int):
self._foreground_bounds_padding = padding
def onMeasure(self, width_measure_spec: int, height_measure_spec: int):
from .view import MeasureSpec
w = MeasureSpec.getSize(width_measure_spec)
h = MeasureSpec.getSize(height_measure_spec)
if self._measure_all_children:
for child in self._children:
child.measure(width_measure_spec, height_measure_spec)
self.setMeasuredDimension(w, h)
def onLayout(self, changed: bool, l: int, t: int, r: int, b: int):
pass
def gatherTransparentRegion(self, region: Any) -> bool:
return False
# ═══════════════════════════════════════════════════════════════
# TableLayout / TableRow
# ═══════════════════════════════════════════════════════════════
class TableLayout(LinearLayout):
"""TableLayout — 表格布局"""
def __init__(self, context=None):
super().__init__(context)
self._stretchable_columns: List[bool] = []
self._shrinkable_columns: List[bool] = []
self._collapsed_columns: List[bool] = []
self._max_columns: int = 0
def setStretchAllColumns(self, stretch_all: bool):
pass
def setShrinkAllColumns(self, shrink_all: bool):
pass
def setColumnStretchable(self, column_index: int, stretchable: bool):
pass
def setColumnShrinkable(self, column_index: int, shrinkable: bool):
pass
def setColumnCollapsed(self, column_index: int, collapsed: bool):
pass
def isColumnStretchable(self, column_index: int) -> bool:
return False
def isColumnShrinkable(self, column_index: int) -> bool:
return False
def isColumnCollapsed(self, column_index: int) -> bool:
return False
class TableRow(LinearLayout):
"""TableRow — 表格行"""
def __init__(self, context=None):
super().__init__(context)
self._layout_params: Dict[int, Any] = {}
class LayoutParams(LinearLayout.LayoutParams):
"""TableRow.LayoutParams — 表格行布局参数"""
def __init__(self, width: int = ViewGroup.LayoutParams.WRAP_CONTENT,
height: int = ViewGroup.LayoutParams.WRAP_CONTENT):
super().__init__(width, height)
self.column = -1
self.span = 1
# ═══════════════════════════════════════════════════════════════
# ScrollView / HorizontalScrollView
# ═══════════════════════════════════════════════════════════════
class ScrollView(FrameLayout):
"""ScrollView — 滚动视图"""
def __init__(self, context=None):
super().__init__(context)
self._fill_viewport = False
self._smooth_scrolling_enabled = True
self._max_scroll_amount = 0
self._scroll_position = 0
self._is_layout_after_scroll = False
def setFillViewport(self, fill_viewport: bool):
self._fill_viewport = fill_viewport
def isFillViewport(self) -> bool:
return self._fill_viewport
def setSmoothScrollingEnabled(self, enabled: bool):
self._smooth_scrolling_enabled = enabled
def isSmoothScrollingEnabled(self) -> bool:
return self._smooth_scrolling_enabled
def fullScroll(self, direction: int) -> bool:
return True
def arrowScroll(self, direction: int) -> bool:
return True
def smoothScrollTo(self, x, y):
self._scroll_x = x
self._scroll_y = y
def smoothScrollBy(self, dx, dy):
self._scroll_x = getattr(self, '_scroll_x', 0) + dx
self._scroll_y = getattr(self, '_scroll_y', 0) + dy
def pageScroll(self, direction: int) -> bool:
return True
def getMaxScrollAmount(self) -> int:
return self._max_scroll_amount
def computeScrollDeltaToGetChildRectOnScreen(self, rect: Any) -> int:
return 0
def isLayoutAfterScroll(self) -> bool:
return self._is_layout_after_scroll
class HorizontalScrollView(FrameLayout):
"""HorizontalScrollView — 水平滚动视图"""
def __init__(self, context=None):
super().__init__(context)
self._fill_viewport = False
def setFillViewport(self, fill_viewport: bool):
self._fill_viewport = fill_viewport
def isFillViewport(self) -> bool:
return self._fill_viewport
def fullScroll(self, direction: int) -> bool:
return True
def arrowScroll(self, direction: int) -> bool:
return True
def smoothScrollTo(self, x, y):
self._scroll_x = x
self._scroll_y = y
def smoothScrollBy(self, dx, dy):
self._scroll_x = getattr(self, '_scroll_x', 0) + dx
self._scroll_y = getattr(self, '_scroll_y', 0) + dy
def pageScroll(self, direction: int) -> bool:
return True
# ═══════════════════════════════════════════════════════════════
# WebView
# ═══════════════════════════════════════════════════════════════
class WebView(ViewGroup):
"""WebView — 网页视图"""
# 布局模式
NORMAL = 0
SMALL_SCREEN = 1
LARGE_SCREEN = 2
def __init__(self, context=None):
super().__init__(context)
self._settings: "WebSettings" = WebSettings()
self._web_view_client: Optional[Callable] = None
self._web_chrome_client: Optional[Callable] = None
self._can_go_back: bool = False
self._can_go_forward: bool = False
self._current_url: str = ""
self._history: List[str] = []
self._current_index: int = -1
self._download_listener: Optional[Callable] = None
self._find_listener: Optional[Callable] = None
self._picture_listener: Optional[Callable] = None
def loadUrl(self, url: str, additional_headers: Optional[Dict[str, str]] = None):
self._current_url = url
self._history = self._history[:self._current_index + 1]
self._history.append(url)
self._current_index = len(self._history) - 1
self._can_go_back = self._current_index > 0
self._can_go_forward = False
def loadData(self, data: str, mime_type: str = "text/html",
encoding: str = "utf-8"):
self._current_url = "about:blank"
def loadDataWithBaseURL(self, base_url: str, data: str, mime_type: str,
encoding: str, history_url: str):
self._current_url = base_url or "about:blank"
def getSettings(self) -> "WebSettings":
return self._settings
def setWebViewClient(self, client: Any):
self._web_view_client = client
def getWebViewClient(self) -> Any:
return self._web_view_client
def setWebChromeClient(self, client: Any):
self._web_chrome_client = client
def getWebChromeClient(self) -> Any:
return self._web_chrome_client
def goBack(self):
if self._can_go_back:
self._current_index -= 1
self._current_url = self._history[self._current_index]
self._can_go_back = self._current_index > 0
self._can_go_forward = True
def goForward(self):
if self._can_go_forward:
self._current_index += 1
self._current_url = self._history[self._current_index]
self._can_go_forward = self._current_index < len(self._history) - 1
self._can_go_back = True
def reload(self):
pass
def canGoBack(self) -> bool:
return self._can_go_back
def canGoForward(self) -> bool:
return self._can_go_forward
def clearHistory(self):
self._history.clear()
self._current_index = -1
self._can_go_back = False
self._can_go_forward = False
def destroy(self):
pass
def onPause(self):
pass
def onResume(self):
pass
def pauseTimers(self):
pass
def resumeTimers(self):
pass
def getUrl(self) -> Optional[str]:
return self._current_url if self._current_url else None
def getTitle(self) -> Optional[str]:
return None
def evaluateJavascript(self, script: str, callback: Optional[Callable] = None):
if callback:
callback("")
def addJavascriptInterface(self, obj: Any, name: str):
pass
def removeJavascriptInterface(self, name: str):
pass
def canGoBackOrForward(self, steps: int) -> bool:
target = self._current_index + steps
return 0 <= target < len(self._history)
def goBackOrForward(self, steps: int):
target = self._current_index + steps
if 0 <= target < len(self._history):
self._current_index = target
self._current_url = self._history[target]
self._can_go_back = target > 0
self._can_go_forward = target < len(self._history) - 1
def setDownloadListener(self, listener: Optional[Callable]):
self._download_listener = listener
def setFindListener(self, listener: Optional[Callable]):
self._find_listener = listener
class WebViewTransport:
def setWebView(self, web_view: "WebView"):
pass
def getWebView(self) -> Optional["WebView"]:
return None
class HitTestResult:
UNKNOWN_TYPE = 0
ANCHOR_TYPE = 1
EDIT_TEXT_TYPE = 2
IMAGE_TYPE = 3
SRC_ANCHOR_TYPE = 4
SRC_IMAGE_ANCHOR_TYPE = 5
PHONE_TYPE = 6
GEO_TYPE = 7
EMAIL_TYPE = 8
def __init__(self):
self._type = WebView.HitTestResult.UNKNOWN_TYPE
self._extra = ""
def getType(self) -> int:
return self._type
def getExtra(self) -> str:
return self._extra
def getHitTestResult(self) -> HitTestResult:
return WebView.HitTestResult()
def findAllAsync(self, find_str: str):
pass
def findNext(self, forward: bool):
pass
def clearMatches(self):
pass
def zoomIn(self) -> bool:
return False
def zoomOut(self) -> bool:
return False
def setInitialScale(self, scale_in_percent: int):
pass
def getScale(self) -> float:
return 1.0
def setPictureListener(self, listener: Optional[Callable]):
self._picture_listener = listener
class WebSettings(Stub):
"""WebSettings — WebView 设置"""
# 渲染模式
RENDER_GOES_BROWSER = 0
RENDER_GOES_SURFACE_VIEW = 1
RENDER_GOES_DISPLAY_LIST = 2
RENDER_GOES_GPU = 3
RENDER_GOES_SURFACE = 4
# 布局算法
LAYOUT_ALGORITHM_NORMAL = 0
LAYOUT_ALGORITHM_SINGLE_COLUMN = 1
LAYOUT_ALGORITHM_NARROW_COLUMNS = 2
LAYOUT_ALGORITHM_TEXT_AUTOSIZING = 3
def __init__(self):
super().__init__()
self._javascript_enabled = False
self._dom_storage_enabled = False
self._allow_file_access = True
self._built_in_zoom_controls = True
self._display_zoom_controls = True
self._load_with_overview_mode = False
self._support_zoom = True
self._use_wide_view_port = False
self._database_enabled = False
self._allow_content_access = True
self._cache_mode = -1 # LOAD_DEFAULT
self._user_agent_string: str = "Mozilla/5.0 (Linux; Android 13)"
self._default_font_size = 16
self._minimum_font_size = 8
self._text_zoom = 100
self._standard_font_family = "sans-serif"
self._serif_font_family = "serif"
self._sans_serif_font_family = "sans-serif"
self._fixed_font_family = "monospace"
self._cursive_font_family = "cursive"
self._fantasy_font_family = "fantasy"
self._default_encoding = "utf-8"
self._layout_algorithm = WebSettings.LAYOUT_ALGORITHM_NORMAL
self._block_network_image = False
self._block_network_loads = False
self._mixed_content_mode = 0 # MIXED_CONTENT_ALWAYS_ALLOW
self._offscreen_pre_raster = False
self._safe_browsing_enabled = True
def setJavaScriptEnabled(self, enabled: bool):
self._javascript_enabled = enabled
def getJavaScriptEnabled(self) -> bool:
return self._javascript_enabled
def setDomStorageEnabled(self, enabled: bool):
self._dom_storage_enabled = enabled
def getDomStorageEnabled(self) -> bool:
return self._dom_storage_enabled
def setAllowFileAccess(self, allow: bool):
self._allow_file_access = allow
def getAllowFileAccess(self) -> bool:
return self._allow_file_access
def setBuiltInZoomControls(self, enabled: bool):
self._built_in_zoom_controls = enabled
def getBuiltInZoomControls(self) -> bool:
return self._built_in_zoom_controls
def setDisplayZoomControls(self, enabled: bool):
self._display_zoom_controls = enabled
def getDisplayZoomControls(self) -> bool:
return self._display_zoom_controls
def setSupportZoom(self, support: bool):
self._support_zoom = support
def supportZoom(self) -> bool:
return self._support_zoom
def setLoadWithOverviewMode(self, overview: bool):
self._load_with_overview_mode = overview
def getLoadWithOverviewMode(self) -> bool:
return self._load_with_overview_mode
def setUseWideViewPort(self, use: bool):
self._use_wide_view_port = use
def getUseWideViewPort(self) -> bool:
return self._use_wide_view_port
def setDatabaseEnabled(self, enabled: bool):
self._database_enabled = enabled
def getDatabaseEnabled(self) -> bool:
return self._database_enabled
def setAllowContentAccess(self, allow: bool):
self._allow_content_access = allow
def getAllowContentAccess(self) -> bool:
return self._allow_content_access
def setCacheMode(self, mode: int):
self._cache_mode = mode
def getCacheMode(self) -> int:
return self._cache_mode
LOAD_DEFAULT = -1
LOAD_CACHE_ELSE_NETWORK = 1
LOAD_NO_CACHE = 2
LOAD_CACHE_ONLY = 3
def setUserAgentString(self, ua: str):
self._user_agent_string = ua
def getUserAgentString(self) -> str:
return self._user_agent_string
def setDefaultFontSize(self, size: int):
self._default_font_size = size
def getDefaultFontSize(self) -> int:
return self._default_font_size
def setMinimumFontSize(self, size: int):
self._minimum_font_size = size
def getMinimumFontSize(self) -> int:
return self._minimum_font_size
def setTextZoom(self, text_zoom: int):
self._text_zoom = text_zoom
def getTextZoom(self) -> int:
return self._text_zoom
def setStandardFontFamily(self, font: str):
self._standard_font_family = font
def setSerifFontFamily(self, font: str):
self._serif_font_family = font
def setSansSerifFontFamily(self, font: str):
self._sans_serif_font_family = font
def setFixedFontFamily(self, font: str):
self._fixed_font_family = font
def setCursiveFontFamily(self, font: str):
self._cursive_font_family = font
def setFantasyFontFamily(self, font: str):
self._fantasy_font_family = font
def setDefaultTextEncodingName(self, encoding: str):
self._default_encoding = encoding
def getDefaultTextEncodingName(self) -> str:
return self._default_encoding
def setLayoutAlgorithm(self, algorithm: int):
self._layout_algorithm = algorithm
def getLayoutAlgorithm(self) -> int:
return self._layout_algorithm
def setBlockNetworkImage(self, block: bool):
self._block_network_image = block
def getBlockNetworkImage(self) -> bool:
return self._block_network_image
def setBlockNetworkLoads(self, block: bool):
self._block_network_loads = block
def getBlockNetworkLoads(self) -> bool:
return self._block_network_loads
def setMixedContentMode(self, mode: int):
self._mixed_content_mode = mode
def getMixedContentMode(self) -> int:
return self._mixed_content_mode
MIXED_CONTENT_ALWAYS_ALLOW = 0
MIXED_CONTENT_NEVER_ALLOW = 1
MIXED_CONTENT_COMPATIBILITY_MODE = 2
def setOffscreenPreRaster(self, enabled: bool):
self._offscreen_pre_raster = enabled
def getOffscreenPreRaster(self) -> bool:
return self._offscreen_pre_raster
def setSafeBrowsingEnabled(self, enabled: bool):
self._safe_browsing_enabled = enabled
def getSafeBrowsingEnabled(self) -> bool:
return self._safe_browsing_enabled
def setNeedInitialFocus(self, flag: bool):
pass
# ═══════════════════════════════════════════════════════════════
# ViewAnimator / ViewFlipper / ViewSwitcher etc.
# ═══════════════════════════════════════════════════════════════
class ViewAnimator(FrameLayout):
"""ViewAnimator — 视图动画容器"""
def __init__(self, context=None):
super().__init__(context)
self._which_child: int = 0
self._animate_first_view: bool = False
self._in_animation: Any = None
self._out_animation: Any = None
def setDisplayedChild(self, which_child: int):
if 0 <= which_child < self.getChildCount():
self._which_child = which_child
def getDisplayedChild(self) -> int:
return self._which_child
def showNext(self):
if self._which_child < self.getChildCount() - 1:
self._which_child += 1
def showPrevious(self):
if self._which_child > 0:
self._which_child -= 1
def setInAnimation(self, in_animation: Any):
self._in_animation = in_animation
def getInAnimation(self) -> Any:
return self._in_animation
def setOutAnimation(self, out_animation: Any):
self._out_animation = out_animation
def getOutAnimation(self) -> Any:
return self._out_animation
def setAnimateFirstView(self, animate: bool):
self._animate_first_view = animate
def getAnimateFirstView(self) -> bool:
return self._animate_first_view
def getBaseline(self) -> int:
return -1
class ViewFlipper(ViewAnimator):
"""ViewFlipper — 视图翻转器"""
def __init__(self, context=None):
super().__init__(context)
self._flip_interval: int = 3000
self._auto_start: bool = False
self._running: bool = False
self._flip_timer: Any = None
def setFlipInterval(self, milliseconds: int):
self._flip_interval = milliseconds
def getFlipInterval(self) -> int:
return self._flip_interval
def setAutoStart(self, auto_start: bool):
self._auto_start = auto_start
def isAutoStart(self) -> bool:
return self._auto_start
def startFlipping(self):
self._running = True
def stopFlipping(self):
self._running = False
def isFlipping(self) -> bool:
return self._running
def onDetachedFromWindow(self):
super().onDetachedFromWindow()
self._running = False
def onAttachedToWindow(self):
super().onAttachedToWindow()
if self._auto_start:
self._running = True
class ViewSwitcher(ViewAnimator):
"""ViewSwitcher — 视图切换器"""
def __init__(self, context=None):
super().__init__(context)
self._factory: Optional[Callable] = None
class ViewFactory:
def makeView(self) -> View:
return View()
def setFactory(self, factory: ViewFactory):
self._factory = factory
def getCurrentView(self) -> Optional[View]:
return self.getChildAt(self._which_child)
def getNextView(self) -> Optional[View]:
next_index = self._which_child + 1
if next_index < self.getChildCount():
return self.getChildAt(next_index)
return None
def setInAnimation(self, in_animation: Any):
super().setInAnimation(in_animation)
def setOutAnimation(self, out_animation: Any):
super().setOutAnimation(out_animation)
class ImageSwitcher(ViewSwitcher):
"""ImageSwitcher — 图片切换器"""
def setImageDrawable(self, drawable: Any):
pass
def setImageResource(self, resid: int):
pass
def setImageURI(self, uri: Any):
pass
def setImageBitmap(self, bitmap: Any):
pass
class TextSwitcher(ViewSwitcher):
"""TextSwitcher — 文本切换器"""
def setCurrentText(self, text: str):
if self.getChildCount() > 0:
child = self.getChildAt(0)
if hasattr(child, "setText"):
child.setText(text)
def setText(self, text: str):
self.setCurrentText(text)
class AdapterViewAnimator(AdapterView):
"""AdapterViewAnimator — 适配器视图动画器"""
def __init__(self, context=None):
super().__init__(context)
self._in_animation: Any = None
self._out_animation: Any = None
self._displayed_child: int = 0
self._loop_views: bool = False
def setDisplayedChild(self, which_child: int):
self._displayed_child = which_child
def getDisplayedChild(self) -> int:
return self._displayed_child
def showNext(self):
self._displayed_child = (self._displayed_child + 1) % max(1, self.getCount())
def showPrevious(self):
last = max(1, self.getCount())
self._displayed_child = (self._displayed_child - 1) % last
def setInAnimation(self, in_animation: Any):
self._in_animation = in_animation
def getInAnimation(self) -> Any:
return self._in_animation
def setOutAnimation(self, out_animation: Any):
self._out_animation = out_animation
def getOutAnimation(self) -> Any:
return self._out_animation
def getCurrentView(self) -> Optional[View]:
return self.getChildAt(self._displayed_child)
def getBaseline(self) -> int:
return -1
def getCount(self) -> int:
if self._adapter:
return self._adapter.getCount()
return 0
class AdapterViewFlipper(AdapterViewAnimator):
"""AdapterViewFlipper — 适配器视图翻转器"""
def __init__(self, context=None):
super().__init__(context)
self._flip_interval: int = 10000
self._auto_start: bool = False
self._running: bool = False
def setFlipInterval(self, milliseconds: int):
self._flip_interval = milliseconds
def getFlipInterval(self) -> int:
return self._flip_interval
def startFlipping(self):
self._running = True
def stopFlipping(self):
self._running = False
def isFlipping(self) -> bool:
return self._running
def setAutoStart(self, auto_start: bool):
self._auto_start = auto_start
def isAutoStart(self) -> bool:
return self._auto_start
class StackView(AdapterViewAnimator):
"""StackView — 栈视图"""
pass
# ═══════════════════════════════════════════════════════════════
# Adapters
# ═══════════════════════════════════════════════════════════════
class BaseAdapter(Stub):
"""BaseAdapter — 基础适配器"""
def __init__(self):
super().__init__()
self._data_set_observable: Any = None
def getCount(self) -> int:
return 0
def getItem(self, position: int) -> Any:
return None
def getItemId(self, position: int) -> int:
return position
def getView(self, position: int, convert_view: Optional[View], parent: Optional[ViewGroup]) -> View:
return View()
def getItemViewType(self, position: int) -> int:
return 0
def getViewTypeCount(self) -> int:
return 1
def hasStableIds(self) -> bool:
return False
def isEmpty(self) -> bool:
return self.getCount() == 0
def areAllItemsEnabled(self) -> bool:
return True
def isEnabled(self, position: int) -> bool:
return True
def getDropDownView(self, position: int, convert_view: Optional[View],
parent: Optional[ViewGroup]) -> View:
return self.getView(position, convert_view, parent)
def notifyDataSetChanged(self):
for observer in getattr(self, '_observers', []):
observer.onChanged()
def notifyDataSetInvalidated(self):
for observer in getattr(self, '_observers', []):
observer.onInvalidated()
def registerDataSetObserver(self, observer):
if not hasattr(self, '_observers'):
self._observers = []
self._observers.append(observer)
def unregisterDataSetObserver(self, observer):
if hasattr(self, '_observers'):
try:
self._observers.remove(observer)
except ValueError:
pass
class ArrayAdapter(BaseAdapter):
"""ArrayAdapter — 数组适配器"""
def __init__(self, context: Any, resource: int, objects: Optional[List[Any]] = None):
super().__init__()
self._context = context
self._resource = resource
self._drop_down_resource = resource
self._objects: List[Any] = list(objects) if objects is not None else []
self._field_id = 0
self._notify_on_change = True
def getCount(self) -> int:
return len(self._objects)
def getItem(self, position: int) -> Any:
if 0 <= position < len(self._objects):
return self._objects[position]
return None
def getPosition(self, item: Any) -> int:
try:
return self._objects.index(item)
except ValueError:
return -1
def getView(self, position: int, convert_view: Optional[View],
parent: Optional[ViewGroup]) -> View:
if convert_view is not None:
tv = convert_view
else:
tv = TextView(self._context)
if hasattr(tv, "setText"):
tv.setText(str(self.getItem(position) if self.getItem(position) is not None else ""))
return tv
def add(self, obj: Any):
self._objects.append(obj)
if self._notify_on_change:
self.notifyDataSetChanged()
def addAll(self, *items: Any):
for item in items:
self._objects.append(item)
if self._notify_on_change:
self.notifyDataSetChanged()
def insert(self, obj: Any, index: int):
self._objects.insert(index, obj)
if self._notify_on_change:
self.notifyDataSetChanged()
def remove(self, obj: Any):
if obj in self._objects:
self._objects.remove(obj)
if self._notify_on_change:
self.notifyDataSetChanged()
def clear(self):
self._objects.clear()
if self._notify_on_change:
self.notifyDataSetChanged()
def sort(self, comparator: Callable):
self._objects.sort(key=comparator)
if self._notify_on_change:
self.notifyDataSetChanged()
def setNotifyOnChange(self, notify_on_change: bool):
self._notify_on_change = notify_on_change
def getContext(self):
return self._context
def getCount(self) -> int:
return len(self._objects)
def setDropDownViewResource(self, resource: int):
self._drop_down_resource = resource
def getDropDownViewResource(self) -> int:
return self._drop_down_resource
class SimpleAdapter(BaseAdapter):
"""SimpleAdapter — 简单适配器"""
def __init__(self, context: Any, data: List[Dict[str, Any]], resource: int,
from_cols: List[str], to_ids: List[int]):
super().__init__()
self._context = context
self._data = data
self._resource = resource
self._from = from_cols
self._to = to_ids
self._drop_down_resource = resource
def getCount(self) -> int:
return len(self._data)
def getItem(self, position: int) -> Any:
if 0 <= position < len(self._data):
return self._data[position]
return None
def getView(self, position: int, convert_view: Optional[View],
parent: Optional[ViewGroup]) -> View:
if convert_view is not None:
v = convert_view
else:
v = View(self._context)
return v
def setDropDownViewResource(self, resource: int):
self._drop_down_resource = resource
class CursorAdapter(BaseAdapter):
"""CursorAdapter — 游标适配器"""
FLAG_AUTO_REQUERY = 0x01
FLAG_REGISTER_CONTENT_OBSERVER = 0x02
def __init__(self, context: Any, c: Any, flags: int = FLAG_AUTO_REQUERY):
super().__init__()
self._context = context
self._cursor = c
self._flags = flags
def getCount(self) -> int:
return 0
def getItem(self, position: int) -> Any:
return None
def getView(self, position: int, convert_view: Optional[View],
parent: Optional[ViewGroup]) -> View:
return View(self._context)
def bindView(self, view: View, context: Any, cursor: Any):
pass
def newView(self, context: Any, cursor: Any, parent: ViewGroup) -> View:
return View(context)
def getCursor(self) -> Any:
return self._cursor
def changeCursor(self, cursor: Any):
self._cursor = cursor
self.notifyDataSetChanged()
def swapCursor(self, new_cursor: Any) -> Any:
old = self._cursor
self._cursor = new_cursor
self.notifyDataSetChanged()
return old
# ═══════════════════════════════════════════════════════════════
# Spinner
# ═══════════════════════════════════════════════════════════════
class Spinner(AdapterView):
"""Spinner — 下拉选择器"""
MODE_DIALOG = 0
MODE_DROPDOWN = 1
def __init__(self, context=None, mode: int = MODE_DROPDOWN):
super().__init__(context)
self._mode = mode
self._prompt: str = ""
self._popup: Any = None
self._on_item_selected_listener: Optional[Callable] = None
class OnItemSelectedListener:
def onItemSelected(self, parent: AdapterView, view: View, position: int, id_: int):
pass
def onNothingSelected(self, parent: AdapterView):
pass
def setOnItemSelectedListener(self, listener: Optional[OnItemSelectedListener]):
self._on_item_selected_listener = listener
def getOnItemSelectedListener(self) -> Optional[OnItemSelectedListener]:
return self._on_item_selected_listener
def setSelection(self, position: int, animate: bool = True):
super().setSelection(position)
def getSelectedItem(self) -> Any:
return super().getSelectedItem()
def getSelectedItemPosition(self) -> int:
return self._selected_position
def getSelectedView(self) -> Optional[View]:
return None
def setPrompt(self, prompt: str):
self._prompt = prompt
def getPrompt(self) -> str:
return self._prompt
def setPopupBackgroundDrawable(self, drawable: Any):
pass
def getPopupBackgroundDrawable(self) -> Any:
return None
def performClick(self) -> bool:
return True
# ═══════════════════════════════════════════════════════════════
# AutoCompleteTextView
# ═══════════════════════════════════════════════════════════════
class AutoCompleteTextView(EditText):
"""AutoCompleteTextView — 自动完成文本"""
def __init__(self, context=None):
super().__init__(context)
self._threshold: int = 2
self._completion_hint: str = ""
self._drop_down_anchor: Optional[View] = None
self._drop_down_horizontal_offset: int = 0
self._drop_down_vertical_offset: int = 0
self._drop_down_width: int = 0
self._drop_down_height: int = 0
self._on_item_click_listener: Optional[Callable] = None
self._on_dismiss_listener: Optional[Callable] = None
self._validator: Optional[Callable] = None
def setThreshold(self, threshold: int):
self._threshold = max(1, threshold)
def getThreshold(self) -> int:
return self._threshold
def setAdapter(self, adapter: Any):
self._adapter = adapter
def getAdapter(self) -> Any:
return self._adapter
def setOnItemClickListener(self, listener: Optional[Callable]):
self._on_item_click_listener = listener
def getOnItemClickListener(self) -> Optional[Callable]:
return self._on_item_click_listener
def setCompletionHint(self, hint: str):
self._completion_hint = hint
def getCompletionHint(self) -> str:
return self._completion_hint
def setDropDownAnchor(self, anchor_id: int):
pass
def setDropDownHorizontalOffset(self, offset: int):
self._drop_down_horizontal_offset = offset
def getDropDownHorizontalOffset(self) -> int:
return self._drop_down_horizontal_offset
def setDropDownVerticalOffset(self, offset: int):
self._drop_down_vertical_offset = offset
def getDropDownVerticalOffset(self) -> int:
return self._drop_down_vertical_offset
def setDropDownWidth(self, width: int):
self._drop_down_width = width
def getDropDownWidth(self) -> int:
return self._drop_down_width
def showDropDown(self):
pass
def dismissDropDown(self):
pass
def isDropDownShowing(self) -> bool:
return False
def enoughToFilter(self) -> bool:
return len(self._text) >= self._threshold
def setValidator(self, validator: Optional[Callable]):
self._validator = validator
def getValidator(self) -> Optional[Callable]:
return self._validator
def performValidation(self):
pass
def setOnDismissListener(self, listener: Optional[Callable]):
self._on_dismiss_listener = listener
def performFiltering(self, text: str, key_code: int):
pass
def onFilterComplete(self, count: int):
pass
class MultiAutoCompleteTextView(AutoCompleteTextView):
"""MultiAutoCompleteTextView — 多值自动完成文本"""
def __init__(self, context=None):
super().__init__(context)
self._tokenizer: Optional[Callable] = None
class CommaTokenizer:
def tokenize(self, text: str) -> List[str]:
return text.split(",")
def findTokenStart(self, text: str, cursor: int) -> int:
return text.rfind(",", 0, cursor) + 1
def findTokenEnd(self, text: str, cursor: int) -> int:
next_comma = text.find(",", cursor)
return next_comma if next_comma >= 0 else len(text)
def terminateToken(self, text: str) -> str:
return text + ","
def setTokenizer(self, tokenizer: Any):
self._tokenizer = tokenizer
def getTokenizer(self) -> Any:
return self._tokenizer
# ═══════════════════════════════════════════════════════════════
# DatePicker / TimePicker / CalendarView / NumberPicker
# ═══════════════════════════════════════════════════════════════
class DatePicker(FrameLayout):
"""DatePicker — 日期选择器"""
def __init__(self, context=None):
super().__init__(context)
self._year: int = 2024
self._month: int = 0 # 0-based
self._day_of_month: int = 1
self._min_date: int = 0
self._max_date: int = 0
self._spinners_shown: bool = True
self._calendar_view_shown: bool = False
self._on_date_changed_listener: Optional[Callable] = None
self._calendar_view: Optional[CalendarView] = None
class OnDateChangedListener:
def onDateChanged(self, view: "DatePicker", year: int, month_of_year: int, day_of_month: int):
pass
def init(self, year: int, month: int, day_of_month: int,
listener: Optional[OnDateChangedListener] = None):
self._year = year
self._month = month
self._day_of_month = day_of_month
self._on_date_changed_listener = listener
if listener:
listener.onDateChanged(self, year, month, day_of_month)
def getYear(self) -> int:
return self._year
def getMonth(self) -> int:
return self._month
def getDayOfMonth(self) -> int:
return self._day_of_month
def setMinDate(self, min_date: int):
self._min_date = min_date
def getMinDate(self) -> int:
return self._min_date
def setMaxDate(self, max_date: int):
self._max_date = max_date
def getMaxDate(self) -> int:
return self._max_date
def setSpinnersShown(self, shown: bool):
self._spinners_shown = shown
def setCalendarViewShown(self, shown: bool):
self._calendar_view_shown = shown
def getCalendarView(self) -> Optional["CalendarView"]:
return self._calendar_view
def setOnDateChangedListener(self, listener: Optional[OnDateChangedListener]):
self._on_date_changed_listener = listener
def updateDate(self, year: int, month: int, day_of_month: int):
self._year = year
self._month = month
self._day_of_month = day_of_month
if self._on_date_changed_listener:
self._on_date_changed_listener.onDateChanged(self, year, month, day_of_month)
class TimePicker(FrameLayout):
"""TimePicker — 时间选择器"""
MODE_CLOCK = 0
MODE_SPINNER = 1
def __init__(self, context=None, mode: int = MODE_CLOCK):
super().__init__(context)
self._current_hour: int = 12
self._current_minute: int = 0
self._is24_hour_view: Optional[bool] = None # None = system default
self._mode = mode
self._on_time_changed_listener: Optional[Callable] = None
class OnTimeChangedListener:
def onTimeChanged(self, view: "TimePicker", hour_of_day: int, minute: int):
pass
def setOnTimeChangedListener(self, listener: Optional[OnTimeChangedListener]):
self._on_time_changed_listener = listener
def getOnTimeChangedListener(self) -> Optional[OnTimeChangedListener]:
return self._on_time_changed_listener
def getCurrentHour(self) -> int:
return self._current_hour
def setCurrentHour(self, current_hour: int):
self._current_hour = current_hour % 24
if self._on_time_changed_listener:
self._on_time_changed_listener.onTimeChanged(self, self._current_hour, self._current_minute)
def getCurrentMinute(self) -> int:
return self._current_minute
def setCurrentMinute(self, current_minute: int):
self._current_minute = current_minute % 60
if self._on_time_changed_listener:
self._on_time_changed_listener.onTimeChanged(self, self._current_hour, self._current_minute)
def setIs24HourView(self, is24_hour_view: Optional[bool]):
self._is24_hour_view = is24_hour_view
def is24HourView(self) -> bool:
if self._is24_hour_view is not None:
return self._is24_hour_view
return False
def getMode(self) -> int:
return self._mode
def setHour(self, hour: int):
self.setCurrentHour(hour)
def getHour(self) -> int:
return self.getCurrentHour()
def setMinute(self, minute: int):
self.setCurrentMinute(minute)
def getMinute(self) -> int:
return self.getCurrentMinute()
class CalendarView(FrameLayout):
"""CalendarView — 日历视图"""
def __init__(self, context=None):
super().__init__(context)
self._date: int = 0
self._min_date: int = 0
self._max_date: int = 0
self._first_day_of_week: int = 1 # Sunday=1
self._week_separator: bool = False
self._on_date_change_listener: Optional[Callable] = None
self._shown_week_count: int = 6
class OnDateChangeListener:
def onSelectedDayChange(self, view: "CalendarView", year: int, month: int, day_of_month: int):
pass
def setOnDateChangeListener(self, listener: Optional[OnDateChangeListener]):
self._on_date_change_listener = listener
def getDate(self) -> int:
return self._date
def setDate(self, date: int, animate: bool = True, center: bool = True):
self._date = date
def getMinDate(self) -> int:
return self._min_date
def setMinDate(self, min_date: int):
self._min_date = min_date
def getMaxDate(self) -> int:
return self._max_date
def setMaxDate(self, max_date: int):
self._max_date = max_date
def getFirstDayOfWeek(self) -> int:
return self._first_day_of_week
def setFirstDayOfWeek(self, first_day_of_week: int):
self._first_day_of_week = first_day_of_week
def setWeekSeparatorDrawable(self, drawable: Any):
self._week_separator = True
def getShownWeekCount(self) -> int:
return self._shown_week_count
def setShownWeekCount(self, count: int):
self._shown_week_count = count
class NumberPicker(ViewGroup):
"""NumberPicker — 数字选择器"""
def __init__(self, context=None):
super().__init__(context)
self._min_value: int = 0
self._max_value: int = 100
self._value: int = 0
self._displayed_values: Optional[List[str]] = None
self._on_value_change_listener: Optional[Callable] = None
self._on_scroll_listener: Optional[Callable] = None
self._wrap_selector_wheel: bool = True
self._selection_divider: bool = True
class OnValueChangeListener:
def onValueChange(self, picker: "NumberPicker", old_val: int, new_val: int):
pass
class OnScrollListener:
def onScrollStateChange(self, picker: "NumberPicker", state: int):
pass
SCROLL_STATE_IDLE = 0
SCROLL_STATE_TOUCH_SCROLL = 1
SCROLL_STATE_FLING = 2
def setMinValue(self, min_value: int):
self._min_value = min_value
if self._value < self._min_value:
self._value = self._min_value
def getMinValue(self) -> int:
return self._min_value
def setMaxValue(self, max_value: int):
self._max_value = max_value
if self._value > self._max_value:
self._value = self._max_value
def getMaxValue(self) -> int:
return self._max_value
def setValue(self, value: int):
old_value = self._value
self._value = max(self._min_value, min(self._max_value, value))
if self._on_value_change_listener and old_value != self._value:
self._on_value_change_listener.onValueChange(self, old_value, self._value)
def getValue(self) -> int:
return self._value
def setDisplayedValues(self, displayed_values: Optional[List[str]]):
self._displayed_values = displayed_values
def getDisplayedValues(self) -> Optional[List[str]]:
return self._displayed_values
def setOnValueChangedListener(self, listener: Optional[OnValueChangeListener]):
self._on_value_change_listener = listener
def setOnScrollListener(self, listener: Optional[OnScrollListener]):
self._on_scroll_listener = listener
def setWrapSelectorWheel(self, wrap: bool):
self._wrap_selector_wheel = wrap
def getWrapSelectorWheel(self) -> bool:
return self._wrap_selector_wheel
def setSelectionDivider(self, divider: Any):
self._selection_divider = True
# ═══════════════════════════════════════════════════════════════
# VideoView / MediaController
# ═══════════════════════════════════════════════════════════════
class VideoView(SurfaceView):
"""VideoView — 视频播放视图"""
def __init__(self, context=None):
super().__init__(context)
self._video_path: Optional[str] = None
self._uri: Any = None
self._media_controller: Optional["MediaController"] = None
self._on_prepared_listener: Optional[Callable] = None
self._on_completion_listener: Optional[Callable] = None
self._on_error_listener: Optional[Callable] = None
self._is_playing: bool = False
self._current_position: int = 0
self._duration: int = 0
self._can_seek_back: bool = True
self._can_seek_forward: bool = True
self._can_pause: bool = True
self._video_width: int = 0
self._video_height: int = 0
def setVideoPath(self, path: str):
self._video_path = path
def setVideoURI(self, uri: Any):
self._uri = uri
def setMediaController(self, controller: "MediaController"):
self._media_controller = controller
def setOnPreparedListener(self, listener: Optional[Callable]):
self._on_prepared_listener = listener
def setOnCompletionListener(self, listener: Optional[Callable]):
self._on_completion_listener = listener
def setOnErrorListener(self, listener: Optional[Callable]):
self._on_error_listener = listener
def start(self):
self._is_playing = True
def stopPlayback(self):
self._is_playing = False
self._current_position = 0
def pause(self):
self._is_playing = False
def resume(self):
self._is_playing = True
def seekTo(self, msec: int):
self._current_position = max(0, min(msec, self._duration))
def getCurrentPosition(self) -> int:
return self._current_position
def getDuration(self) -> int:
return self._duration
def isPlaying(self) -> bool:
return self._is_playing
def canSeekForward(self) -> bool:
return self._can_seek_forward
def canSeekBackward(self) -> bool:
return self._can_seek_back
def canPause(self) -> bool:
return self._can_pause
def getBufferPercentage(self) -> int:
return 100
def resolveAdjustedSize(self, desired_size: int, measure_spec: int) -> int:
from .view import MeasureSpec
return MeasureSpec.getSize(measure_spec)
def setVideoWidth(self, video_width: int):
self._video_width = video_width
def setVideoHeight(self, video_height: int):
self._video_height = video_height
@property
def videoWidth(self) -> int:
return self._video_width
@property
def videoHeight(self) -> int:
return self._video_height
class MediaController(FrameLayout):
"""MediaController — 媒体控制器"""
def __init__(self, context=None, target: Optional[VideoView] = None):
super().__init__(context)
self._target = target
self._anchor_view: Optional[View] = None
self._is_showing: bool = False
self._set_up_listener: Optional[Callable] = None
def setMediaPlayer(self, player: "MediaController.MediaPlayerControl"):
pass
def setAnchorView(self, view: View):
self._anchor_view = view
def show(self, timeout: int = 3000):
self._is_showing = True
def hide(self):
self._is_showing = False
def isShowing(self) -> bool:
return self._is_showing
def setEnabled(self, enabled: bool):
super().setEnabled(enabled)
def setOnPrepareListener(self, listener: Optional[Callable]):
self._set_up_listener = listener
class MediaPlayerControl:
def start(self):
pass
def pause(self):
pass
def getDuration(self) -> int:
return 0
def getCurrentPosition(self) -> int:
return 0
def seekTo(self, pos: int):
pass
def isPlaying(self) -> bool:
return False
def getBufferPercentage(self) -> int:
return 0
def canPause(self) -> bool:
return False
def canSeekBackward(self) -> bool:
return False
def canSeekForward(self) -> bool:
return False
def getAudioSessionId(self) -> int:
return 0
# ═══════════════════════════════════════════════════════════════
# Toast
# ═══════════════════════════════════════════════════════════════
class Toast(Stub):
"""Toast — 提示消息"""
LENGTH_SHORT = 0
LENGTH_LONG = 1
def __init__(self, context=None):
super().__init__()
if context is None:
context = android_context
self._context = context
self._duration = Toast.LENGTH_SHORT
self._gravity = Gravity.NO_GRAVITY
self._x_offset: int = 0
self._y_offset: int = 0
self._view: Optional[View] = None
self._text: str = ""
self._horizontal_margin: float = 0.0
self._vertical_margin: float = 0.0
self._next_view: Optional[View] = None
@staticmethod
def makeText(context: Any, text: str, duration: int) -> "Toast":
toast = Toast(context)
toast._text = text
toast._duration = duration
return toast
def show(self):
if _USE_LINUX:
LinuxNotify.send("Toast", self._text)
else:
print(f"[Toast] {self._text}")
def cancel(self):
if _USE_LINUX:
# 桌面通知自动消失,无需操作
return
pass
def setGravity(self, gravity: int, x_offset: int, y_offset: int):
self._gravity = gravity
self._x_offset = x_offset
self._y_offset = y_offset
def setDuration(self, duration: int):
self._duration = duration
def getDuration(self) -> int:
return self._duration
def setView(self, view: View):
self._view = view
def getView(self) -> Optional[View]:
return self._view
def setMargin(self, horizontal_margin: float, vertical_margin: float):
self._horizontal_margin = horizontal_margin
self._vertical_margin = vertical_margin
def getHorizontalMargin(self) -> float:
return self._horizontal_margin
def getVerticalMargin(self) -> float:
return self._vertical_margin
def setText(self, text: str):
self._text = text
def setNextAnnouncementView(self, view: View):
self._next_view = view
def getNextAnnouncementView(self) -> Optional[View]:
return self._next_view
def isTouchable(self) -> bool:
return False
# ═══════════════════════════════════════════════════════════════
# PopupMenu / PopupWindow
# ═══════════════════════════════════════════════════════════════
class PopupMenu(Stub):
"""PopupMenu — 弹出菜单"""
def __init__(self, context: Any, anchor: View, gravity: int = Gravity.NO_GRAVITY):
super().__init__()
self._context = context
self._anchor = anchor
self._gravity = gravity
self._menu: "PopupMenu.Menu" = PopupMenu.Menu()
self._on_menu_item_click_listener: Optional[Callable] = None
self._on_dismiss_listener: Optional[Callable] = None
class Menu(Stub):
"""PopupMenu.Menu — 弹出菜单项列表"""
def __init__(self):
super().__init__()
self._items: List[Dict] = []
def add(self, title: str) -> Any:
item_id = len(self._items)
self._items.append({"id": item_id, "title": title, "enabled": True})
return item_id
def findItem(self, id_: int) -> Optional[Dict]:
for item in self._items:
if item["id"] == id_:
return item
return None
def removeItem(self, id_: int):
for i, item in enumerate(self._items):
if item["id"] == id_:
self._items.pop(i)
break
def size(self) -> int:
return len(self._items)
def getItem(self, index: int) -> Optional[Dict]:
if 0 <= index < len(self._items):
return self._items[index]
return None
def clear(self):
self._items.clear()
def setOnMenuItemClickListener(self, listener: Optional[Callable]):
pass
def getMenu(self) -> "PopupMenu.Menu":
return self._menu
def inflate(self, menu_res_id: int):
pass
def show(self):
pass
def dismiss(self):
pass
def setOnMenuItemClickListener(self, listener: Optional[Callable]):
self._on_menu_item_click_listener = listener
def setOnDismissListener(self, listener: Optional[Callable]):
self._on_dismiss_listener = listener
def getDragToOpenListener(self) -> Optional[Callable]:
return None
def setForceShowIcon(self, force_show: bool):
pass
class PopupWindow(Stub):
"""PopupWindow — 弹出窗口"""
def __init__(self, content_view: Optional[View] = None,
width: int = ViewGroup.LayoutParams.WRAP_CONTENT,
height: int = ViewGroup.LayoutParams.WRAP_CONTENT,
focusable: bool = True):
super().__init__()
self._content_view = content_view
self._width = width
self._height = height
self._focusable = focusable
self._is_showing: bool = False
self._outside_touchable: bool = False
self._touchable: bool = True
self._background_drawable: Any = None
self._animation_style: int = -1
self._elevation: float = 0.0
self._input_method_mode: int = -1
self._soft_input_mode: int = -1
self._on_dismiss_listener: Optional[Callable] = None
self._window_layout_type: int = 1000 # TYPE_APPLICATION_PANEL
self._overlap_anchor: bool = True
self._clip_to_screen: bool = True
self._split_enabled: bool = False
self._width_mode: int = 0
self._height_mode: int = 0
def showAsDropDown(self, anchor: View, x_off: int = 0, y_off: int = 0,
gravity: int = Gravity.NO_GRAVITY):
self._is_showing = True
def showAtLocation(self, parent: View, gravity: int, x: int, y: int):
self._is_showing = True
def dismiss(self):
self._is_showing = False
if self._on_dismiss_listener:
self._on_dismiss_listener()
def isShowing(self) -> bool:
return self._is_showing
def setContentView(self, content_view: View):
self._content_view = content_view
def getContentView(self) -> Optional[View]:
return self._content_view
def setWidth(self, width: int):
self._width = width
def getWidth(self) -> int:
return self._width
def setHeight(self, height: int):
self._height = height
def getHeight(self) -> int:
return self._height
def setFocusable(self, focusable: bool):
self._focusable = focusable
def isFocusable(self) -> bool:
return self._focusable
def setOutsideTouchable(self, touchable: bool):
self._outside_touchable = touchable
def isOutsideTouchable(self) -> bool:
return self._outside_touchable
def setTouchable(self, touchable: bool):
self._touchable = touchable
def isTouchable(self) -> bool:
return self._touchable
def setBackgroundDrawable(self, background: Any):
self._background_drawable = background
def getBackground(self) -> Any:
return self._background_drawable
def setAnimationStyle(self, anim_style: int):
self._animation_style = anim_style
def getAnimationStyle(self) -> int:
return self._animation_style
def setElevation(self, elevation: float):
self._elevation = elevation
def getElevation(self) -> float:
return self._elevation
def setOnDismissListener(self, listener: Optional[Callable]):
self._on_dismiss_listener = listener
def setTouchInterceptor(self, interceptor: Optional[Callable]):
pass
def setClippingEnabled(self, enabled: bool):
self._clip_to_screen = enabled
def isClippingEnabled(self) -> bool:
return self._clip_to_screen
def setSplitTouchEnabled(self, enabled: bool):
self._split_enabled = enabled
def isSplitTouchEnabled(self) -> bool:
return self._split_enabled
def setWindowLayoutType(self, layout_type: int):
self._window_layout_type = layout_type
def getWindowLayoutType(self) -> int:
return self._window_layout_type
def setSoftInputMode(self, mode: int):
self._soft_input_mode = mode
def getSoftInputMode(self) -> int:
return self._soft_input_mode
def setInputMethodMode(self, mode: int):
self._input_method_mode = mode
def getInputMethodMode(self) -> int:
return self._input_method_mode
INPUT_METHOD_FROM_FOCUSABLE = 0
INPUT_METHOD_NEEDED = 1
INPUT_METHOD_NOT_NEEDED = 2
# ═══════════════════════════════════════════════════════════════
# Toolbar
# ═══════════════════════════════════════════════════════════════
class Toolbar(ViewGroup):
"""Toolbar — 工具栏"""
def __init__(self, context=None):
super().__init__(context)
self._title: str = ""
self._subtitle: str = ""
self._navigation_icon: Any = None
self._navigation_content_description: str = ""
self._logo: Any = None
self._logo_description: str = ""
self._title_text_color: int = 0
self._subtitle_text_color: int = 0
self._collapse_icon: Any = None
self._menu: Any = None
self._on_menu_item_click_listener: Optional[Callable] = None
self._navigation_on_click_listener: Optional[Callable] = None
self._popup_theme: int = 0
self._content_inset_start: int = 16
self._content_inset_end: int = 16
self._title_margin_start: int = 0
def setTitle(self, title: str):
self._title = title
def getTitle(self) -> str:
return self._title
def setSubtitle(self, subtitle: str):
self._subtitle = subtitle
def getSubtitle(self) -> str:
return self._subtitle
def setNavigationIcon(self, icon: Any):
self._navigation_icon = icon
def getNavigationIcon(self) -> Any:
return self._navigation_icon
def setNavigationContentDescription(self, description: str):
self._navigation_content_description = description
def getNavigationContentDescription(self) -> str:
return self._navigation_content_description
def setLogo(self, logo: Any):
self._logo = logo
def getLogo(self) -> Any:
return self._logo
def setLogoDescription(self, description: str):
self._logo_description = description
def getLogoDescription(self) -> str:
return self._logo_description
def setTitleTextColor(self, color: int):
self._title_text_color = color
def setSubtitleTextColor(self, color: int):
self._subtitle_text_color = color
def inflateMenu(self, menu_res_id: int):
pass
def getMenu(self) -> Any:
return self._menu
def setOnMenuItemClickListener(self, listener: Optional[Callable]):
self._on_menu_item_click_listener = listener
def setNavigationOnClickListener(self, listener: Optional[Callable]):
self._navigation_on_click_listener = listener
def setCollapseIcon(self, icon: Any):
self._collapse_icon = icon
def getCollapseIcon(self) -> Any:
return self._collapse_icon
def setPopupTheme(self, res_id: int):
self._popup_theme = res_id
def getPopupTheme(self) -> int:
return self._popup_theme
def setContentInsetsRelative(self, content_inset_start: int, content_inset_end: int):
self._content_inset_start = content_inset_start
self._content_inset_end = content_inset_end
def getContentInsetStart(self) -> int:
return self._content_inset_start
def getContentInsetEnd(self) -> int:
return self._content_inset_end
def collapseActionView(self):
pass
def dismissPopupMenus(self):
pass
def isOverflowMenuShowing(self) -> bool:
return False
def showOverflowMenu(self) -> bool:
return False
def hideOverflowMenu(self) -> bool:
return False
def getOverflowIcon(self) -> Any:
return None
def setOverflowIcon(self, icon: Any):
pass
class ActionMenuView(LinearLayout):
"""ActionMenuView — 操作菜单视图"""
pass
# ═══════════════════════════════════════════════════════════════
# TabHost / TabWidget
# ═══════════════════════════════════════════════════════════════
class TabHost(FrameLayout):
"""TabHost — 标签主机"""
def __init__(self, context=None):
super().__init__(context)
self._tab_widget: Optional["TabWidget"] = None
self._tab_content: Optional[FrameLayout] = None
self._tabs: List["TabHost.TabSpec"] = []
self._current_tab: int = 0
self._on_tab_changed_listener: Optional[Callable] = None
self._local_activity_manager: Any = None
class TabSpec(Stub):
"""TabHost.TabSpec — 标签规格"""
def __init__(self, tag: str):
super().__init__()
self._tag = tag
self._indicator: Any = None
self._content: Any = None
self._content_view: Optional[View] = None
def setIndicator(self, indicator: Any) -> "TabHost.TabSpec":
self._indicator = indicator
return self
def setContent(self, content: Any) -> "TabHost.TabSpec":
self._content = content
return self
def setContent(self, view_id: int) -> "TabHost.TabSpec":
return self
def setContent(self, view: View) -> "TabHost.TabSpec":
self._content_view = view
return self
def getTag(self) -> str:
return self._tag
def getIndicator(self) -> Any:
return self._indicator
class OnTabChangeListener:
def onTabChanged(self, tab_id: str):
pass
def setup(self):
pass
def newTabSpec(self, tag: str) -> "TabHost.TabSpec":
spec = TabHost.TabSpec(tag)
self._tabs.append(spec)
return spec
def addTab(self, tab_spec: "TabHost.TabSpec"):
if tab_spec not in self._tabs:
self._tabs.append(tab_spec)
def setCurrentTab(self, index: int):
if 0 <= index < len(self._tabs):
self._current_tab = index
def setCurrentTabByTag(self, tag: str):
for i, tab in enumerate(self._tabs):
if tab.getTag() == tag:
self._current_tab = i
break
def getCurrentTab(self) -> int:
return self._current_tab
def getCurrentTabTag(self) -> str:
if 0 <= self._current_tab < len(self._tabs):
return self._tabs[self._current_tab].getTag()
return ""
def getTabWidget(self) -> "TabWidget":
return self._tab_widget
def getTabContentView(self) -> FrameLayout:
return self._tab_content
def setOnTabChangedListener(self, listener: Optional[OnTabChangeListener]):
self._on_tab_changed_listener = listener
def clearAllTabs(self):
self._tabs.clear()
self._current_tab = 0
class TabWidget(LinearLayout):
"""TabWidget — 标签部件"""
def __init__(self, context=None):
super().__init__(context)
self._strip: Any = None
self._left_strip: Any = None
self._right_strip: Any = None
self._selected_tab: int = -1
self._draw_full_strip: bool = False
def setStripEnabled(self, strip_enabled: bool):
self._draw_full_strip = strip_enabled
def isStripEnabled(self) -> bool:
return self._draw_full_strip
def setLeftStripDrawable(self, drawable: Any):
self._left_strip = drawable
def setRightStripDrawable(self, drawable: Any):
self._right_strip = drawable
def setDividerDrawable(self, drawable: Any):
pass
def focusCurrentTab(self, index: int):
self._selected_tab = index
def isEnabled(self) -> bool:
return True
# ═══════════════════════════════════════════════════════════════
# ExpandableListView
# ═══════════════════════════════════════════════════════════════
class ExpandableListView(ListView):
"""ExpandableListView — 可展开列表视图"""
PACKED_POSITION_TYPE_GROUP = 0
PACKED_POSITION_TYPE_CHILD = 1
PACKED_POSITION_TYPE_NULL = 2
PACKED_POSITION_VALUE_NULL = 0xFFFFFFFF
def __init__(self, context=None):
super().__init__(context)
self._expandable_adapter: Optional["ExpandableListAdapter"] = None
self._on_group_collapse_listener: Optional[Callable] = None
self._on_group_expand_listener: Optional[Callable] = None
self._on_child_click_listener: Optional[Callable] = None
self._on_group_click_listener: Optional[Callable] = None
def setAdapter(self, adapter: "ExpandableListAdapter"):
self._expandable_adapter = adapter
self._adapter = adapter
def getExpandableListAdapter(self) -> Optional["ExpandableListAdapter"]:
return self._expandable_adapter
def setOnGroupCollapseListener(self, listener: Optional[Callable]):
self._on_group_collapse_listener = listener
def setOnGroupExpandListener(self, listener: Optional[Callable]):
self._on_group_expand_listener = listener
def setOnChildClickListener(self, listener: Optional[Callable]):
self._on_child_click_listener = listener
def setOnGroupClickListener(self, listener: Optional[Callable]):
self._on_group_click_listener = listener
def expandGroup(self, group_pos: int, animate: bool = True) -> bool:
return True
def collapseGroup(self, group_pos: int) -> bool:
return True
def setOnItemClickListener(self, listener: Optional[Callable]):
self._on_item_click_listener = listener
def getOnItemClickListener(self) -> Optional[Callable]:
return self._on_item_click_listener
def setOnItemLongClickListener(self, listener: Optional[Callable]):
self._on_item_long_click_listener = listener
def getOnItemLongClickListener(self) -> Optional[Callable]:
return self._on_item_long_click_listener
def getPackedPositionType(self, packed_position: int) -> int:
if packed_position == ExpandableListView.PACKED_POSITION_VALUE_NULL:
return ExpandableListView.PACKED_POSITION_TYPE_NULL
return ExpandableListView.PACKED_POSITION_TYPE_GROUP
def getPackedPositionGroup(self, packed_position: int) -> int:
return packed_position & 0xFFFF
def getPackedPositionChild(self, packed_position: int) -> int:
return (packed_position >> 16) & 0xFFFF
@staticmethod
def getPackedPositionForChild(group_position: int, child_position: int) -> int:
return (child_position << 16) | group_position
@staticmethod
def getPackedPositionForGroup(group_position: int) -> int:
return group_position
def isGroupExpanded(self, group_position: int) -> bool:
return False
def setSelectedGroup(self, group_position: int):
pass
def setSelectedChild(self, group_position: int, child_position: int, should_expand_group: bool):
pass
def smoothScrollToPosition(self, position: int):
pass
# ═══════════════════════════════════════════════════════════════
# ExpandableListAdapter
# ═══════════════════════════════════════════════════════════════
class ExpandableListAdapter(Stub):
"""ExpandableListAdapter — 可扩展列表适配器接口"""
def __init__(self):
super().__init__()
def getGroupCount(self) -> int:
raise AndroidOnlyError("ExpandableListAdapter.getGroupCount: not implemented")
def getChildrenCount(self, group_position: int) -> int:
raise AndroidOnlyError("ExpandableListAdapter.getChildrenCount: not implemented")
def getGroup(self, group_position: int) -> Any:
raise AndroidOnlyError("ExpandableListAdapter.getGroup: not implemented")
def getChild(self, group_position: int, child_position: int) -> Any:
raise AndroidOnlyError("ExpandableListAdapter.getChild: not implemented")
def getGroupId(self, group_position: int) -> int:
raise AndroidOnlyError("ExpandableListAdapter.getGroupId: not implemented")
def getChildId(self, group_position: int, child_position: int) -> int:
raise AndroidOnlyError("ExpandableListAdapter.getChildId: not implemented")
def hasStableIds(self) -> bool:
return False
def getGroupView(self, group_position: int, is_expanded: bool,
convert_view: Optional[Any], parent: Optional[Any]) -> Any:
raise AndroidOnlyError("ExpandableListAdapter.getGroupView: not implemented")
def getChildView(self, group_position: int, child_position: int,
is_last_child: bool, convert_view: Optional[Any],
parent: Optional[Any]) -> Any:
raise AndroidOnlyError("ExpandableListAdapter.getChildView: not implemented")
def isChildSelectable(self, group_position: int, child_position: int) -> bool:
return True
def areAllItemsEnabled(self) -> bool:
return True
def isEmpty(self) -> bool:
return self.getGroupCount() == 0
def onGroupCollapsed(self, group_position: int):
pass
def onGroupExpanded(self, group_position: int):
pass
def getCombinedGroupId(self, group_id: int) -> int:
return group_id
def getCombinedChildId(self, group_id: int, child_id: int) -> int:
return (group_id << 32) | child_id
# ═══════════════════════════════════════════════════════════════
# SimpleExpandableListAdapter
# ═══════════════════════════════════════════════════════════════
class SimpleExpandableListAdapter(ExpandableListAdapter, BaseAdapter):
"""SimpleExpandableListAdapter — 简单的可展开列表适配器"""
def __init__(self, context: Any,
group_data: List[Dict[str, Any]],
group_layout: int,
group_from: List[str],
group_to: List[int],
child_data: List[List[Dict[str, Any]]],
child_layout: int,
child_from: List[str],
child_to: List[int]):
super().__init__()
self._context = context
self._group_data = group_data
self._group_layout = group_layout
self._group_from = group_from
self._group_to = group_to
self._child_data = child_data
self._child_layout = child_layout
self._child_from = child_from
self._child_to = child_to
def getGroupCount(self) -> int:
return len(self._group_data)
def getChildrenCount(self, group_position: int) -> int:
if 0 <= group_position < len(self._child_data):
return len(self._child_data[group_position])
return 0
def getGroup(self, group_position: int) -> Any:
if 0 <= group_position < len(self._group_data):
return self._group_data[group_position]
return None
def getChild(self, group_position: int, child_position: int) -> Any:
if 0 <= group_position < len(self._child_data):
children = self._child_data[group_position]
if 0 <= child_position < len(children):
return children[child_position]
return None
def getGroupId(self, group_position: int) -> int:
return group_position
def getChildId(self, group_position: int, child_position: int) -> int:
return child_position
def getGroupView(self, group_position: int, is_expanded: bool,
convert_view: Optional[Any], parent: Optional[Any]) -> Any:
if convert_view is not None:
view = convert_view
else:
view = TextView(self._context)
view.setText(str(self.getGroup(group_position)))
return view
def getChildView(self, group_position: int, child_position: int,
is_last_child: bool, convert_view: Optional[Any],
parent: Optional[Any]) -> Any:
if convert_view is not None:
view = convert_view
else:
view = TextView(self._context)
child = self.getChild(group_position, child_position)
view.setText(str(child) if child else "")
return view
def isChildSelectable(self, group_position: int, child_position: int) -> bool:
return True
def getCount(self) -> int:
return self.getGroupCount()
def getItem(self, position: int) -> Any:
return self.getGroup(position)
def getItemId(self, position: int) -> int:
return position
def getView(self, position: int, convert_view: Optional[Any],
parent: Optional[Any]) -> Any:
return self.getGroupView(position, False, convert_view, parent)
# ═══════════════════════════════════════════════════════════════
# BaseExpandableListAdapter
# ═══════════════════════════════════════════════════════════════
class BaseExpandableListAdapter(ExpandableListAdapter, BaseAdapter):
"""BaseExpandableListAdapter — 可展开列表适配器基类"""
def __init__(self):
super().__init__()
def getGroupCount(self) -> int:
return 0
def getChildrenCount(self, group_position: int) -> int:
return 0
def getGroup(self, group_position: int) -> Any:
return None
def getChild(self, group_position: int, child_position: int) -> Any:
return None
def getGroupId(self, group_position: int) -> int:
return group_position
def getChildId(self, group_position: int, child_position: int) -> int:
return child_position
def getGroupView(self, group_position: int, is_expanded: bool,
convert_view: Optional[Any], parent: Optional[Any]) -> Any:
if convert_view is not None:
view = convert_view
else:
view = TextView(android_context)
view.setText(f"Group {group_position}")
return view
def getChildView(self, group_position: int, child_position: int,
is_last_child: bool, convert_view: Optional[Any],
parent: Optional[Any]) -> Any:
if convert_view is not None:
view = convert_view
else:
view = TextView(android_context)
view.setText(f"Child {group_position}:{child_position}")
return view
def isChildSelectable(self, group_position: int, child_position: int) -> bool:
return True
def areAllItemsEnabled(self) -> bool:
return True
def onGroupCollapsed(self, group_position: int):
pass
def onGroupExpanded(self, group_position: int):
pass
def notifyDataSetChanged(self):
pass
def notifyDataSetInvalidated(self):
pass
def getCount(self) -> int:
return self.getGroupCount()
def getItem(self, position: int) -> Any:
return self.getGroup(position)
def getItemId(self, position: int) -> int:
return position
def getView(self, position: int, convert_view: Optional[Any],
parent: Optional[Any]) -> Any:
return self.getGroupView(position, False, convert_view, parent)
# ═══════════════════════════════════════════════════════════════
# SearchView
# ═══════════════════════════════════════════════════════════════
class SearchView(LinearLayout):
"""SearchView — 搜索视图"""
def __init__(self, context=None):
super().__init__(context)
self._query: str = ""
self._query_hint: Optional[str] = None
self._iconified: bool = True
self._iconified_by_default: bool = True
self._submit_button_enabled: bool = False
self._query_refinement_enabled: bool = False
self._searchable_info: Any = None
self._on_query_text_listener: Optional[Callable] = None
self._on_close_listener: Optional[Callable] = None
self._on_suggestion_listener: Optional[Callable] = None
self._on_query_text_focus_change_listener: Optional[Callable] = None
self._suggestion_adapter: Optional["CursorAdapter"] = None
self._max_width: int = sys.maxsize
self._on_search_click_listener: Optional[Callable] = None
class OnQueryTextListener:
def onQueryTextSubmit(self, query: str) -> bool:
return False
def onQueryTextChange(self, new_text: str) -> bool:
return False
class OnCloseListener:
def onClose(self) -> bool:
return False
class OnSuggestionListener:
def onSuggestionSelect(self, position: int) -> bool:
return False
def onSuggestionClick(self, position: int) -> bool:
return False
def setQuery(self, query: str, submit: bool = False):
self._query = query
if self._on_query_text_listener:
self._on_query_text_listener.onQueryTextChange(query)
if submit and self._on_query_text_listener:
self._on_query_text_listener.onQueryTextSubmit(query)
def getQuery(self) -> str:
return self._query
def setQueryHint(self, hint: Optional[str]):
self._query_hint = hint
def getQueryHint(self) -> Optional[str]:
return self._query_hint
def setIconified(self, iconified: bool):
self._iconified = iconified
def isIconified(self) -> bool:
return self._iconified
def setIconifiedByDefault(self, iconified: bool):
self._iconified_by_default = iconified
def isIconifiedByDefault(self) -> bool:
return self._iconified_by_default
def setSubmitButtonEnabled(self, enabled: bool):
self._submit_button_enabled = enabled
def isSubmitButtonEnabled(self) -> bool:
return self._submit_button_enabled
def setQueryRefinementEnabled(self, enable: bool):
self._query_refinement_enabled = enable
def isQueryRefinementEnabled(self) -> bool:
return self._query_refinement_enabled
def setOnQueryTextListener(self, listener: Optional["SearchView.OnQueryTextListener"]):
self._on_query_text_listener = listener
def getOnQueryTextListener(self) -> Optional["SearchView.OnQueryTextListener"]:
return self._on_query_text_listener
def setOnCloseListener(self, listener: Optional["SearchView.OnCloseListener"]):
self._on_close_listener = listener
def setOnSuggestionListener(self, listener: Optional["SearchView.OnSuggestionListener"]):
self._on_suggestion_listener = listener
def setOnSearchClickListener(self, listener: Optional[Callable]):
self._on_search_click_listener = listener
def setSuggestionsAdapter(self, adapter: Optional["CursorAdapter"]):
self._suggestion_adapter = adapter
def getSuggestionsAdapter(self) -> Optional["CursorAdapter"]:
return self._suggestion_adapter
def setMaxWidth(self, max_width: int):
self._max_width = max_width
def getMaxWidth(self) -> int:
return self._max_width
def onActionViewExpanded(self):
self._iconified = False
def onActionViewCollapsed(self):
self._iconified = True
def onClose(self) -> bool:
if self._on_close_listener:
return self._on_close_listener.onClose()
self._iconified = self._iconified_by_default
return True
def clearFocus(self):
pass
# ═══════════════════════════════════════════════════════════════
# ZoomControls
# ═══════════════════════════════════════════════════════════════
class ZoomControls(LinearLayout):
"""ZoomControls — 缩放控件"""
def __init__(self, context=None):
super().__init__(context)
self._zoom_in_enabled: bool = True
self._zoom_out_enabled: bool = True
self._zoom_speed: int = 300
self._on_zoom_in_listener: Optional[Callable] = None
self._on_zoom_out_listener: Optional[Callable] = None
def show(self):
self.setVisibility(View.VISIBLE)
def hide(self):
self.setVisibility(View.GONE)
def setIsZoomInEnabled(self, is_enabled: bool):
self._zoom_in_enabled = is_enabled
def setIsZoomOutEnabled(self, is_enabled: bool):
self._zoom_out_enabled = is_enabled
def setOnZoomInClickListener(self, listener: Optional[Callable]):
self._on_zoom_in_listener = listener
def setOnZoomOutClickListener(self, listener: Optional[Callable]):
self._on_zoom_out_listener = listener
def setZoomSpeed(self, speed: int):
self._zoom_speed = speed
def getZoomSpeed(self) -> int:
return self._zoom_speed
def hasFocus(self) -> bool:
return False
# ═══════════════════════════════════════════════════════════════
# DigitalClock
# ═══════════════════════════════════════════════════════════════
class DigitalClock(TextView):
"""DigitalClock — 数字时钟(API 14+ 已弃用)"""
def __init__(self, context=None):
super().__init__(context)
self._format: str = "%H:%M:%S"
self._update_interval: int = 1000
self._running: bool = False
self._timer: Optional[Any] = None
def start(self):
self._running = True
def stop(self):
self._running = False
def setFormat(self, fmt: str):
self._format = fmt
def getFormat(self) -> str:
return self._format
def onTimeChanged(self):
now = datetime.now().strftime(self._format) if hasattr(datetime, 'now') else "00:00:00"
self.setText(now)
# ═══════════════════════════════════════════════════════════════
# AnalogClock
# ═══════════════════════════════════════════════════════════════
class AnalogClock(View):
"""AnalogClock — 模拟时钟(API 14+ 已弃用)"""
def __init__(self, context=None):
super().__init__(context)
self._dial: Optional[Any] = None
self._hand_hour: Optional[Any] = None
self._hand_minute: Optional[Any] = None
self._running: bool = False
self._update_interval: int = 1000
def start(self):
self._running = True
self.invalidate()
def stop(self):
self._running = False
def onDraw(self, canvas: Any):
super().onDraw(canvas)
def setDialDrawable(self, drawable: Any):
self._dial = drawable
def setHourDrawable(self, drawable: Any):
self._hand_hour = drawable
def setMinuteDrawable(self, drawable: Any):
self._hand_minute = drawable
# ═══════════════════════════════════════════════════════════════
# Chronometer
# ═══════════════════════════════════════════════════════════════
class Chronometer(TextView):
"""Chronometer — 计时器"""
def __init__(self, context=None):
super().__init__(context)
self._base: int = 0
self._format: Optional[str] = None
self._running: bool = False
self._visible: bool = True
self._on_chronometer_tick_listener: Optional[Callable] = None
self._count_down: bool = False
self._elapsed: float = 0.0
def start(self):
self._running = True
def stop(self):
self._running = False
def setBase(self, base: int):
self._base = base
def getBase(self) -> int:
return self._base
def setFormat(self, fmt: str):
self._format = fmt
def getFormat(self) -> Optional[str]:
return self._format
def setOnChronometerTickListener(self, listener: Optional[Callable]):
self._on_chronometer_tick_listener = listener
def getOnChronometerTickListener(self) -> Optional[Callable]:
return self._on_chronometer_tick_listener
def isCountDown(self) -> bool:
return self._count_down
def setCountDown(self, count_down: bool):
self._count_down = count_down
def getElapsedRealtime(self) -> float:
return self._elapsed
def setVisibility(self, visibility: int):
self._visible = (visibility == View.VISIBLE)
super().setVisibility(visibility)
def onDraw(self, canvas: Any):
super().onDraw(canvas)
if self._running and self._on_chronometer_tick_listener:
try:
self._on_chronometer_tick_listener(self)
except Exception:
pass
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
# TextView hierarchy
"TextView", "EditText", "Button", "ImageButton", "CheckBox",
"RadioButton", "ToggleButton", "Switch",
# ImageView
"ImageView",
# Progress
"ProgressBar", "SeekBar", "RatingBar",
# AdapterView hierarchy
"AdapterView", "ListView", "GridView", "ExpandableListView",
"Spinner", "AutoCompleteTextView", "MultiAutoCompleteTextView",
# RecyclerView
"RecyclerView", "RecyclerViewAdapter", "RecyclerViewViewHolder",
"RecyclerViewLayoutManager", "LinearLayoutManager",
"GridLayoutManager", "StaggeredGridLayoutManager",
# Layouts
"LinearLayout", "RelativeLayout", "FrameLayout",
"TableLayout", "TableRow",
# Scrolling
"ScrollView", "HorizontalScrollView",
# WebView
"WebView", "WebSettings",
# Adapters
"BaseAdapter", "ArrayAdapter", "SimpleAdapter", "CursorAdapter",
"ExpandableListAdapter", "SimpleExpandableListAdapter",
"BaseExpandableListAdapter",
# ViewAnimator
"ViewAnimator", "ViewFlipper", "ViewSwitcher",
"ImageSwitcher", "TextSwitcher",
"AdapterViewAnimator", "StackView", "AdapterViewFlipper",
# Date/Time
"DatePicker", "TimePicker", "CalendarView", "NumberPicker",
"DigitalClock", "AnalogClock", "Chronometer",
# Media
"VideoView", "MediaController",
# Toast/Popup
"Toast", "PopupMenu", "PopupWindow",
# Toolbar
"Toolbar", "ActionMenuView",
# Tabs
"TabHost", "TabWidget",
# Search
"SearchView",
# Zoom
"ZoomControls",
]