类继承冲突修复 (7个文件, 8组冲突): - view_ext.py: 16个类继承view.py基础版本 (ViewOutlineProvider, ViewPropertyAnimator, ViewTreeObserver, ViewConfiguration, ViewParent, WindowInsets, Display, DragEvent, GestureDetector, ScaleGestureDetector, SoundEffectConstants, Surface, SurfaceView, TextureView, VelocityTracker, WindowInsetsController) - hardware_sensor_ext.py: 3个类继承hardware.py (Sensor, SensorEvent, SensorEventListener) - app_fragment.py: 3个类继承app.py (Fragment, FragmentManager, FragmentTransaction) - content_intent.py: 3个类继承content.py/app.py (Intent, PendingIntent, IntentFilter) - widget_ext.py: 29个类继承widget.py (18个单继承+11个多继承, 修复super()链) - os_power.py: 2个类继承os.py (BatteryManager, PowerManager) - net_ext.py: 12个类继承net.py (11个单继承+1个多继承) __all__导出补全 (36个类): - java.net: InterfaceAddress - android.database: MatrixCursor, SQLiteStatement - android.view: GestureDetector, ScaleGestureDetector, SurfaceHolder, Surface, SurfaceView, TextureView, ViewOutlineProvider, WindowManager, Window, LayoutInflater, Menu, MenuItem, ContextMenu, Display, DisplayMetrics - android.graphics: Region, PathMeasure, NinePatch, Picture, PictureDrawable, Camera, PorterDuff, PorterDuffXfermode, Shader, LinearGradient, RadialGradient, SweepGradient, BitmapShader, ComposeShader, CornerPathEffect, DashPathEffect, PathDashPathEffect, SumPathEffect - android.widget: LinearLayout, FrameLayout, RelativeLayout, TableLayout, TableRow, BaseAdapter, ArrayAdapter, SimpleAdapter, CursorAdapter, ImageButton, ToggleButton, RadioButton, SeekBar, RatingBar, ViewSwitcher, TextSwitcher, ImageSwitcher, SearchView, NumberPicker, Chronometer, TabHost, TabWidget, ViewAnimator, ZoomControls, DigitalClock, AnalogClock 统计: 221模块, 3666类, 31005方法, 183629行
2232 lines
71 KiB
Python
2232 lines
71 KiB
Python
"""
|
|
Android Widget 扩展组件 — AbsoluteLayout, AdapterView, ProgressBar, SeekBar, Layouts 等
|
|
方法签名完全对齐 Android SDK
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
import time
|
|
import threading
|
|
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
|
|
|
|
from .._core import Stub, AndroidOnlyError
|
|
from .widget import AdapterView as _BaseAdapterView
|
|
from .widget import AutoCompleteTextView as _BaseAutoCompleteTextView
|
|
from .widget import ProgressBar as _BaseProgressBar
|
|
from .widget import Chronometer as _BaseChronometer
|
|
from .widget import TableLayout as _BaseTableLayout
|
|
from .widget import TableRow as _BaseTableRow
|
|
from .widget import LinearLayout as _BaseLinearLayout
|
|
from .widget import FrameLayout as _BaseFrameLayout
|
|
from .widget import RelativeLayout as _BaseRelativeLayout
|
|
from .widget import NumberPicker as _BaseNumberPicker
|
|
from .widget import SearchView as _BaseSearchView
|
|
from .widget import TabHost as _BaseTabHost
|
|
from .widget import TabWidget as _BaseTabWidget
|
|
from .widget import ViewAnimator as _BaseViewAnimator
|
|
from .widget import ImageButton as _BaseImageButton
|
|
from .widget import ZoomControls as _BaseZoomControls
|
|
from .widget import DigitalClock as _BaseDigitalClock
|
|
from .widget import AnalogClock as _BaseAnalogClock
|
|
from .widget import ToggleButton as _BaseToggleButton
|
|
from .widget import RadioButton as _BaseRadioButton
|
|
from .widget import SeekBar as _BaseSeekBar
|
|
from .widget import RatingBar as _BaseRatingBar
|
|
from .widget import ViewSwitcher as _BaseViewSwitcher
|
|
from .widget import TextSwitcher as _BaseTextSwitcher
|
|
from .widget import ImageSwitcher as _BaseImageSwitcher
|
|
from .widget import BaseAdapter as _BaseBaseAdapter
|
|
from .widget import ArrayAdapter as _BaseArrayAdapter
|
|
from .widget import SimpleAdapter as _BaseSimpleAdapter
|
|
from .widget import CursorAdapter as _BaseCursorAdapter
|
|
from ..java.lang import RuntimeException
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# 布局常量
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
VISIBLE = 0
|
|
INVISIBLE = 4
|
|
GONE = 8
|
|
|
|
MATCH_PARENT = -1
|
|
WRAP_CONTENT = -2
|
|
|
|
FILL_PARENT = -1
|
|
|
|
HORIZONTAL = 0
|
|
VERTICAL = 1
|
|
|
|
LEFT = 0
|
|
RIGHT = 1
|
|
CENTER = 2
|
|
TOP = 3
|
|
BOTTOM = 4
|
|
|
|
NO_ID = -1
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AbsoluteLayout
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AbsoluteLayout(Stub):
|
|
"""AbsoluteLayout — 绝对布局"""
|
|
|
|
class LayoutParams(Stub):
|
|
"""AbsoluteLayout.LayoutParams — 绝对布局参数"""
|
|
|
|
def __init__(self, width: int = WRAP_CONTENT, height: int = WRAP_CONTENT,
|
|
x: int = 0, y: int = 0):
|
|
super().__init__()
|
|
self.width = width
|
|
self.height = height
|
|
self.x = x
|
|
self.y = y
|
|
|
|
def toString(self) -> str:
|
|
return (f"AbsoluteLayout.LayoutParams({self.width}x{self.height} "
|
|
f"@{self.x},{self.y})")
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._children: List[Any] = []
|
|
|
|
def addView(self, child: Any, x: int = 0, y: int = 0) -> None:
|
|
self._children.append(child)
|
|
|
|
def toString(self) -> str:
|
|
return f"AbsoluteLayout(children={len(self._children)})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Adapter / AdapterView / Filter / Filterable
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Adapter(Stub):
|
|
"""Adapter — 适配器接口"""
|
|
|
|
IGNORE_ITEM_VIEW_TYPE = -1
|
|
NO_SELECTION = -2147483648
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._data_set_observers: List[Callable] = []
|
|
|
|
def registerDataSetObserver(self, observer: Callable) -> None:
|
|
if observer not in self._data_set_observers:
|
|
self._data_set_observers.append(observer)
|
|
|
|
def unregisterDataSetObserver(self, observer: Callable) -> None:
|
|
if observer in self._data_set_observers:
|
|
self._data_set_observers.remove(observer)
|
|
|
|
def notifyDataSetChanged(self) -> None:
|
|
for obs in list(self._data_set_observers):
|
|
obs()
|
|
|
|
def notifyDataSetInvalidated(self) -> None:
|
|
for obs in list(self._data_set_observers):
|
|
obs()
|
|
|
|
def getCount(self) -> int:
|
|
return 0
|
|
|
|
def getItem(self, position: int) -> Optional[Any]:
|
|
return None
|
|
|
|
def getItemId(self, position: int) -> int:
|
|
return position
|
|
|
|
def getItemViewType(self, position: int) -> int:
|
|
return Adapter.IGNORE_ITEM_VIEW_TYPE
|
|
|
|
def getViewTypeCount(self) -> int:
|
|
return 1
|
|
|
|
def hasStableIds(self) -> bool:
|
|
return False
|
|
|
|
def isEmpty(self) -> bool:
|
|
return self.getCount() == 0
|
|
|
|
def getView(self, position: int, convert_view: Optional[Any],
|
|
parent: Any) -> Optional[Any]:
|
|
return None
|
|
|
|
def getDropDownView(self, position: int, convert_view: Optional[Any],
|
|
parent: Any) -> Optional[Any]:
|
|
return self.getView(position, convert_view, parent)
|
|
|
|
def toString(self) -> str:
|
|
return f"Adapter(count={self.getCount()})"
|
|
|
|
|
|
class AdapterView(_BaseAdapterView):
|
|
"""AdapterView — 适配器视图"""
|
|
|
|
ITEM_VIEW_TYPE_IGNORE = -1
|
|
ITEM_VIEW_TYPE_HEADER_OR_FOOTER = -2
|
|
|
|
INVALID_POSITION = -1
|
|
INVALID_ROW_ID = -9223372036854775808
|
|
INVALID_TYPE = -1
|
|
|
|
CHOICE_MODE_NONE = 0
|
|
CHOICE_MODE_SINGLE = 1
|
|
CHOICE_MODE_MULTIPLE = 2
|
|
CHOICE_MODE_MODAL = 3
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._adapter: Optional[Adapter] = None
|
|
self._choice_mode: int = self.CHOICE_MODE_NONE
|
|
self._checked_positions: Set[int] = set()
|
|
self._selected_position: int = self.INVALID_POSITION
|
|
self._item_click_listener: Optional[Callable] = None
|
|
self._item_long_click_listener: Optional[Callable] = None
|
|
self._item_selected_listener: Optional[Callable] = None
|
|
self._empty_view: Optional[Any] = None
|
|
self._first_visible_position: int = 0
|
|
self._last_visible_position: int = 0
|
|
self._lock = threading.Lock()
|
|
|
|
def setAdapter(self, adapter: Optional[Adapter]) -> None:
|
|
self._adapter = adapter
|
|
|
|
def getAdapter(self) -> Optional[Adapter]:
|
|
return self._adapter
|
|
|
|
def setOnItemClickListener(self, listener: Optional[Callable]) -> None:
|
|
self._item_click_listener = listener
|
|
|
|
def getOnItemClickListener(self) -> Optional[Callable]:
|
|
return self._item_click_listener
|
|
|
|
def setOnItemLongClickListener(self, listener: Optional[Callable]) -> None:
|
|
self._item_long_click_listener = listener
|
|
|
|
def setOnItemSelectedListener(self, listener: Optional[Callable]) -> None:
|
|
self._item_selected_listener = listener
|
|
|
|
def getSelectedItemPosition(self) -> int:
|
|
return self._selected_position
|
|
|
|
def getSelectedItemId(self) -> int:
|
|
if self._adapter and self._selected_position != self.INVALID_POSITION:
|
|
return self._adapter.getItemId(self._selected_position)
|
|
return self.INVALID_ROW_ID
|
|
|
|
def getSelectedView(self) -> Optional[Any]:
|
|
return None
|
|
|
|
def getSelectedItem(self) -> Optional[Any]:
|
|
if self._adapter and self._selected_position != self.INVALID_POSITION:
|
|
return self._adapter.getItem(self._selected_position)
|
|
return None
|
|
|
|
def setSelection(self, position: int) -> None:
|
|
self._selected_position = position
|
|
|
|
def getFirstVisiblePosition(self) -> int:
|
|
return self._first_visible_position
|
|
|
|
def getLastVisiblePosition(self) -> int:
|
|
return self._last_visible_position
|
|
|
|
def getCount(self) -> int:
|
|
return self._adapter.getCount() if self._adapter else 0
|
|
|
|
def getChildAt(self, index: int) -> Optional[Any]:
|
|
return None
|
|
|
|
def getChildCount(self) -> int:
|
|
return 0
|
|
|
|
def setChoiceMode(self, mode: int) -> None:
|
|
self._choice_mode = mode
|
|
|
|
def getChoiceMode(self) -> int:
|
|
return self._choice_mode
|
|
|
|
def setItemChecked(self, position: int, checked: bool) -> None:
|
|
with self._lock:
|
|
if checked:
|
|
if self._choice_mode == self.CHOICE_MODE_SINGLE:
|
|
self._checked_positions.clear()
|
|
self._checked_positions.add(position)
|
|
else:
|
|
self._checked_positions.discard(position)
|
|
|
|
def isItemChecked(self, position: int) -> bool:
|
|
return position in self._checked_positions
|
|
|
|
def getCheckedItemPosition(self) -> int:
|
|
if self._checked_positions:
|
|
return next(iter(self._checked_positions))
|
|
return self.INVALID_POSITION
|
|
|
|
def getCheckedItemPositions(self) -> Set[int]:
|
|
return set(self._checked_positions)
|
|
|
|
def getCheckedItemIds(self) -> Set[int]:
|
|
ids = set()
|
|
for pos in self._checked_positions:
|
|
if self._adapter:
|
|
ids.add(self._adapter.getItemId(pos))
|
|
return ids
|
|
|
|
def getCheckItemCount(self) -> int:
|
|
return len(self._checked_positions)
|
|
|
|
def setEmptyView(self, view: Any) -> None:
|
|
self._empty_view = view
|
|
|
|
def getEmptyView(self) -> Optional[Any]:
|
|
return self._empty_view
|
|
|
|
def performItemClick(self, view: Any, position: int, id_val: int) -> bool:
|
|
if self._item_click_listener:
|
|
self._item_click_listener(self, view, position, id_val)
|
|
return True
|
|
return False
|
|
|
|
def toString(self) -> str:
|
|
return (f"AdapterView(adapter={'yes' if self._adapter else 'no'}, "
|
|
f"count={self.getCount()})")
|
|
|
|
|
|
class Filter(Stub):
|
|
"""Filter — 过滤器"""
|
|
|
|
class FilterResults(Stub):
|
|
"""Filter.FilterResults — 过滤结果"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.values: Optional[Any] = None
|
|
self.count: int = 0
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._filter_listener: Optional[Callable] = None
|
|
|
|
def filter(self, constraint: str) -> None:
|
|
results = self.performFiltering(constraint)
|
|
self.publishResults(constraint, results)
|
|
|
|
def filter(self, constraint: str, listener: Optional[Callable]) -> None:
|
|
self._filter_listener = listener
|
|
self.filter(constraint)
|
|
|
|
def performFiltering(self, constraint: str) -> FilterResults:
|
|
return Filter.FilterResults()
|
|
|
|
def publishResults(self, constraint: str, results: FilterResults) -> None:
|
|
pass
|
|
|
|
def convertResultToString(self, result_value: Any) -> str:
|
|
return str(result_value) if result_value is not None else ""
|
|
|
|
def toString(self) -> str:
|
|
return "Filter()"
|
|
|
|
|
|
class Filterable(Stub):
|
|
"""Filterable — 可过滤接口"""
|
|
|
|
def getFilter(self) -> Filter:
|
|
return Filter()
|
|
|
|
|
|
class ThemedSpinnerAdapter(Stub):
|
|
"""ThemedSpinnerAdapter — 主题化 Spinner 适配器"""
|
|
|
|
def setDropDownViewTheme(self, theme: Any) -> None:
|
|
pass
|
|
|
|
def getDropDownViewTheme(self) -> Optional[Any]:
|
|
return None
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AutoCompleteTextView
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AutoCompleteTextView(_BaseAutoCompleteTextView):
|
|
"""AutoCompleteTextView — 自动完成文本视图"""
|
|
|
|
THRESHOLD_DEFAULT = 2
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._threshold = self.THRESHOLD_DEFAULT
|
|
self._adapter: Optional[Adapter] = None
|
|
self._listener: Optional[Callable] = None
|
|
self._validator: Optional[Callable] = None
|
|
self._completion_hint: Optional[str] = None
|
|
self._drop_down_width: int = WRAP_CONTENT
|
|
self._drop_down_height: int = WRAP_CONTENT
|
|
self._drop_down_anchor: Optional[Any] = None
|
|
self._drop_down_horizontal_offset: int = 0
|
|
self._drop_down_vertical_offset: int = 0
|
|
self._showing = False
|
|
|
|
def setThreshold(self, threshold: int) -> None:
|
|
self._threshold = max(1, threshold)
|
|
|
|
def getThreshold(self) -> int:
|
|
return self._threshold
|
|
|
|
def setAdapter(self, adapter: Adapter) -> None:
|
|
self._adapter = adapter
|
|
|
|
def getAdapter(self) -> Optional[Adapter]:
|
|
return self._adapter
|
|
|
|
def setOnItemClickListener(self, listener: Optional[Callable]) -> None:
|
|
self._listener = listener
|
|
|
|
def setOnItemSelectedListener(self, listener: Optional[Callable]) -> None:
|
|
pass
|
|
|
|
def showDropDown(self) -> None:
|
|
self._showing = True
|
|
|
|
def dismissDropDown(self) -> None:
|
|
self._showing = False
|
|
|
|
def isShowing(self) -> bool:
|
|
return self._showing
|
|
|
|
def setDropDownWidth(self, width: int) -> None:
|
|
self._drop_down_width = width
|
|
|
|
def getDropDownWidth(self) -> int:
|
|
return self._drop_down_width
|
|
|
|
def setDropDownHeight(self, height: int) -> None:
|
|
self._drop_down_height = height
|
|
|
|
def getDropDownHeight(self) -> int:
|
|
return self._drop_down_height
|
|
|
|
def setDropDownAnchor(self, anchor: Any) -> None:
|
|
self._drop_down_anchor = anchor
|
|
|
|
def getDropDownAnchor(self) -> Optional[Any]:
|
|
return self._drop_down_anchor
|
|
|
|
def setDropDownHorizontalOffset(self, offset: int) -> None:
|
|
self._drop_down_horizontal_offset = offset
|
|
|
|
def getDropDownHorizontalOffset(self) -> int:
|
|
return self._drop_down_horizontal_offset
|
|
|
|
def setDropDownVerticalOffset(self, offset: int) -> None:
|
|
self._drop_down_vertical_offset = offset
|
|
|
|
def getDropDownVerticalOffset(self) -> int:
|
|
return self._drop_down_vertical_offset
|
|
|
|
def setCompletionHint(self, hint: str) -> None:
|
|
self._completion_hint = hint
|
|
|
|
def getCompletionHint(self) -> Optional[str]:
|
|
return self._completion_hint
|
|
|
|
def setValidator(self, validator: Optional[Callable]) -> None:
|
|
self._validator = validator
|
|
|
|
def getValidator(self) -> Optional[Callable]:
|
|
return self._validator
|
|
|
|
def performValidation(self) -> None:
|
|
if self._validator:
|
|
self._validator()
|
|
|
|
def enoughToFilter(self) -> bool:
|
|
return True
|
|
|
|
def toString(self) -> str:
|
|
return f"AutoCompleteTextView(threshold={self._threshold})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# ProgressBar / SeekBar / AbsSeekBar / RatingBar
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class ProgressBar(_BaseProgressBar):
|
|
"""ProgressBar — 进度条扩展"""
|
|
|
|
STYLE_HORIZONTAL = 0x00000001
|
|
STYLE_SPINNER = 0x00000000
|
|
|
|
def __init__(self, context: Any = None, style: int = STYLE_SPINNER):
|
|
super().__init__()
|
|
self._min: int = 0
|
|
self._max: int = 100
|
|
self._progress: int = 0
|
|
self._secondary_progress: int = 0
|
|
self._indeterminate = (style == self.STYLE_SPINNER)
|
|
self._indeterminate_drawable: Optional[Any] = None
|
|
self._progress_drawable: Optional[Any] = None
|
|
self._on_progress_listener: Optional[Callable] = None
|
|
self._mirror = False
|
|
|
|
def setMin(self, min_val: int) -> None:
|
|
self._min = min_val
|
|
if self._progress < min_val:
|
|
self._progress = min_val
|
|
|
|
def getMin(self) -> int:
|
|
return self._min
|
|
|
|
def setMax(self, max_val: int) -> None:
|
|
self._max = max_val
|
|
if self._progress > max_val:
|
|
self._progress = max_val
|
|
|
|
def getMax(self) -> int:
|
|
return self._max
|
|
|
|
def setProgress(self, progress: int) -> None:
|
|
self._progress = max(self._min, min(self._max, progress))
|
|
if self._on_progress_listener:
|
|
self._on_progress_listener(self, self._progress)
|
|
|
|
def getProgress(self) -> int:
|
|
return self._progress
|
|
|
|
def setSecondaryProgress(self, progress: int) -> None:
|
|
self._secondary_progress = max(self._min, min(self._max, progress))
|
|
|
|
def getSecondaryProgress(self) -> int:
|
|
return self._secondary_progress
|
|
|
|
def incrementProgressBy(self, diff: int) -> None:
|
|
self.setProgress(self._progress + diff)
|
|
|
|
def incrementSecondaryProgressBy(self, diff: int) -> None:
|
|
self._secondary_progress = max(self._min, min(self._max, self._secondary_progress + diff))
|
|
|
|
def setIndeterminate(self, indeterminate: bool) -> None:
|
|
self._indeterminate = indeterminate
|
|
|
|
def isIndeterminate(self) -> bool:
|
|
return self._indeterminate
|
|
|
|
def setIndeterminateDrawable(self, drawable: Any) -> None:
|
|
self._indeterminate_drawable = drawable
|
|
|
|
def getIndeterminateDrawable(self) -> Optional[Any]:
|
|
return self._indeterminate_drawable
|
|
|
|
def setProgressDrawable(self, drawable: Any) -> None:
|
|
self._progress_drawable = drawable
|
|
|
|
def getProgressDrawable(self) -> Optional[Any]:
|
|
return self._progress_drawable
|
|
|
|
def setOnProgressBarChangeListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_progress_listener = listener
|
|
|
|
def setMirrorForRtl(self, mirror: bool) -> None:
|
|
self._mirror = mirror
|
|
|
|
def getMirrorForRtl(self) -> bool:
|
|
return self._mirror
|
|
|
|
def getProgressScale(self) -> float:
|
|
if self._max <= self._min:
|
|
return 0.0
|
|
return (self._progress - self._min) / (self._max - self._min)
|
|
|
|
def toString(self) -> str:
|
|
return (f"ProgressBar(min={self._min}, max={self._max}, "
|
|
f"progress={self._progress}, "
|
|
f"indeterminate={self._indeterminate})")
|
|
|
|
|
|
class AbsSeekBar(ProgressBar):
|
|
"""AbsSeekBar — 抽象进度条"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__(context, ProgressBar.STYLE_HORIZONTAL)
|
|
self._thumb: Optional[Any] = None
|
|
self._thumb_offset: int = 0
|
|
self._key_progress_increment: int = 1
|
|
self._is_dragging = False
|
|
self._on_seek_bar_change: Optional[Callable] = None
|
|
self._split_track: bool = False
|
|
self._tick_mark: Optional[Any] = None
|
|
|
|
def setThumb(self, thumb: Any) -> None:
|
|
self._thumb = thumb
|
|
|
|
def getThumb(self) -> Optional[Any]:
|
|
return self._thumb
|
|
|
|
def setThumbOffset(self, offset: int) -> None:
|
|
self._thumb_offset = offset
|
|
|
|
def getThumbOffset(self) -> int:
|
|
return self._thumb_offset
|
|
|
|
def setKeyProgressIncrement(self, increment: int) -> None:
|
|
self._key_progress_increment = max(1, increment)
|
|
|
|
def getKeyProgressIncrement(self) -> int:
|
|
return self._key_progress_increment
|
|
|
|
def setSplitTrack(self, split: bool) -> None:
|
|
self._split_track = split
|
|
|
|
def getSplitTrack(self) -> bool:
|
|
return self._split_track
|
|
|
|
def setTickMark(self, mark: Any) -> None:
|
|
self._tick_mark = mark
|
|
|
|
def getTickMark(self) -> Optional[Any]:
|
|
return self._tick_mark
|
|
|
|
def setOnSeekBarChangeListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_seek_bar_change = listener
|
|
|
|
|
|
class SeekBar(_BaseSeekBar, AbsSeekBar):
|
|
"""SeekBar — 拖动条"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
AbsSeekBar.__init__(self, context)
|
|
self._on_seek_bar_change: Optional[Callable] = None
|
|
|
|
def setOnSeekBarChangeListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_seek_bar_change = listener
|
|
|
|
def getOnSeekBarChangeListener(self) -> Optional[Callable]:
|
|
return self._on_seek_bar_change
|
|
|
|
def toString(self) -> str:
|
|
return f"SeekBar(progress={self.getProgress()})"
|
|
|
|
|
|
class RatingBar(_BaseRatingBar, ProgressBar):
|
|
"""RatingBar — 评分条"""
|
|
|
|
def __init__(self, context: Any = None, num_stars: int = 5,
|
|
step_size: float = 0.5):
|
|
ProgressBar.__init__(self, context, ProgressBar.STYLE_HORIZONTAL)
|
|
self._num_stars = num_stars
|
|
self._step_size = step_size
|
|
self._rating: float = 0.0
|
|
self._is_indicator = False
|
|
self._on_rating_change: Optional[Callable] = None
|
|
self.setMax(num_stars)
|
|
|
|
def setNumStars(self, num: int) -> None:
|
|
self._num_stars = num
|
|
self.setMax(num)
|
|
|
|
def getNumStars(self) -> int:
|
|
return self._num_stars
|
|
|
|
def setRating(self, rating: float) -> None:
|
|
self._rating = max(0.0, min(float(self._num_stars), rating))
|
|
self.setProgress(int(self._rating))
|
|
|
|
def getRating(self) -> float:
|
|
return self._rating
|
|
|
|
def setStepSize(self, size: float) -> None:
|
|
self._step_size = max(0.1, size)
|
|
|
|
def getStepSize(self) -> float:
|
|
return self._step_size
|
|
|
|
def setIsIndicator(self, indicator: bool) -> None:
|
|
self._is_indicator = indicator
|
|
|
|
def isIndicator(self) -> bool:
|
|
return self._is_indicator
|
|
|
|
def setOnRatingBarChangeListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_rating_change = listener
|
|
|
|
def getOnRatingBarChangeListener(self) -> Optional[Callable]:
|
|
return self._on_rating_change
|
|
|
|
def toString(self) -> str:
|
|
return f"RatingBar(rating={self._rating}/{self._num_stars})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Chronometer
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Chronometer(_BaseChronometer):
|
|
"""Chronometer — 计时器"""
|
|
|
|
FORMAT_ELAPSED_TIME = 1
|
|
FORMAT_SECONDS = 2
|
|
FORMAT_MINUTES = 3
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._base: int = 0
|
|
self._elapsed: int = 0
|
|
self._running = False
|
|
self._format: str = "%H:%M:%S"
|
|
self._on_tick: Optional[Callable] = None
|
|
self._lock = threading.Lock()
|
|
|
|
def setBase(self, base: int) -> None:
|
|
self._base = base
|
|
|
|
def getBase(self) -> int:
|
|
return self._base
|
|
|
|
def start(self) -> None:
|
|
with self._lock:
|
|
self._running = True
|
|
|
|
def stop(self) -> None:
|
|
with self._lock:
|
|
self._running = False
|
|
self._elapsed = int(time.time() * 1000) - self._base
|
|
|
|
def setFormat(self, format_str: str) -> None:
|
|
self._format = format_str
|
|
|
|
def getFormat(self) -> str:
|
|
return self._format
|
|
|
|
def setOnChronometerTickListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_tick = listener
|
|
|
|
def getOnChronometerTickListener(self) -> Optional[Callable]:
|
|
return self._on_tick
|
|
|
|
def isRunning(self) -> bool:
|
|
return self._running
|
|
|
|
def getElapsedTime(self) -> int:
|
|
if self._running:
|
|
return int(time.time() * 1000) - self._base
|
|
return self._elapsed
|
|
|
|
def toString(self) -> str:
|
|
return f"Chronometer(running={self._running})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# CompoundButton / ToggleButton / RadioButton / RadioGroup
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class CompoundButton(Stub):
|
|
"""CompoundButton — 复合按钮"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._checked = False
|
|
self._on_check_listener: Optional[Callable] = None
|
|
self._button_drawable: Optional[Any] = None
|
|
|
|
def toggle(self) -> None:
|
|
self.setChecked(not self._checked)
|
|
|
|
def setChecked(self, checked: bool) -> None:
|
|
if self._checked != checked:
|
|
self._checked = checked
|
|
if self._on_check_listener:
|
|
self._on_check_listener(self, checked)
|
|
|
|
def isChecked(self) -> bool:
|
|
return self._checked
|
|
|
|
def setOnCheckedChangeListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_check_listener = listener
|
|
|
|
def getOnCheckedChangeListener(self) -> Optional[Callable]:
|
|
return self._on_check_listener
|
|
|
|
def setButtonDrawable(self, drawable: Any) -> None:
|
|
self._button_drawable = drawable
|
|
|
|
def getButtonDrawable(self) -> Optional[Any]:
|
|
return self._button_drawable
|
|
|
|
def toString(self) -> str:
|
|
return f"CompoundButton(checked={self._checked})"
|
|
|
|
|
|
class ToggleButton(_BaseToggleButton, CompoundButton):
|
|
"""ToggleButton — 开关按钮"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__(context)
|
|
CompoundButton.__init__(self, context)
|
|
self._text_on: str = "ON"
|
|
self._text_off: str = "OFF"
|
|
|
|
def setTextOn(self, text: str) -> None:
|
|
self._text_on = text
|
|
|
|
def getTextOn(self) -> str:
|
|
return self._text_on
|
|
|
|
def setTextOff(self, text: str) -> None:
|
|
self._text_off = text
|
|
|
|
def getTextOff(self) -> str:
|
|
return self._text_off
|
|
|
|
def toString(self) -> str:
|
|
return f"ToggleButton(checked={self.isChecked()})"
|
|
|
|
|
|
class RadioButton(_BaseRadioButton, CompoundButton):
|
|
"""RadioButton — 单选按钮"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__(context)
|
|
CompoundButton.__init__(self, context)
|
|
|
|
def toggle(self) -> None:
|
|
if not self.isChecked():
|
|
super().toggle()
|
|
|
|
def toString(self) -> str:
|
|
return f"RadioButton(checked={self.isChecked()})"
|
|
|
|
|
|
class RadioGroup(Stub):
|
|
"""RadioGroup — 单选按钮组"""
|
|
|
|
class LayoutParams(Stub):
|
|
"""RadioGroup.LayoutParams — 单选按钮组布局参数"""
|
|
|
|
def __init__(self, width: int = WRAP_CONTENT, height: int = WRAP_CONTENT):
|
|
super().__init__()
|
|
self.width = width
|
|
self.height = height
|
|
|
|
def toString(self) -> str:
|
|
return f"RadioGroup.LayoutParams({self.width}x{self.height})"
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._checked_id: int = NO_ID
|
|
self._on_check_listener: Optional[Callable] = None
|
|
self._children: List[RadioButton] = []
|
|
self._protect_from_checked_change = False
|
|
|
|
def setOnCheckedChangeListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_check_listener = listener
|
|
|
|
def getOnCheckedChangeListener(self) -> Optional[Callable]:
|
|
return self._on_check_listener
|
|
|
|
def check(self, id_val: int) -> None:
|
|
self._checked_id = id_val
|
|
if self._on_check_listener:
|
|
self._on_check_listener(self, id_val)
|
|
|
|
def getCheckedRadioButtonId(self) -> int:
|
|
return self._checked_id
|
|
|
|
def clearCheck(self) -> None:
|
|
self._checked_id = NO_ID
|
|
|
|
def addView(self, child: RadioButton, index: int = -1) -> None:
|
|
if index < 0 or index >= len(self._children):
|
|
self._children.append(child)
|
|
else:
|
|
self._children.insert(index, child)
|
|
|
|
def getChildCount(self) -> int:
|
|
return len(self._children)
|
|
|
|
def toString(self) -> str:
|
|
return f"RadioGroup(checkedId={self._checked_id})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# TableLayout / TableRow
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class TableLayout(_BaseTableLayout):
|
|
"""TableLayout — 表格布局"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._rows: List[TableRow] = []
|
|
self._columns_stretchable: Set[int] = set()
|
|
self._columns_collapsible: Set[int] = set()
|
|
self._shrink_allowed = False
|
|
self._stretch_allowed = False
|
|
|
|
def addView(self, child: Any, index: int = -1) -> None:
|
|
pass
|
|
|
|
def setColumnStretchable(self, column_index: int, stretchable: bool) -> None:
|
|
if stretchable:
|
|
self._columns_stretchable.add(column_index)
|
|
else:
|
|
self._columns_stretchable.discard(column_index)
|
|
|
|
def setColumnCollapsible(self, column_index: int, collapsible: bool) -> None:
|
|
if collapsible:
|
|
self._columns_collapsible.add(column_index)
|
|
else:
|
|
self._columns_collapsible.discard(column_index)
|
|
|
|
def setShrinkAllColumns(self, shrink: bool) -> None:
|
|
self._shrink_allowed = shrink
|
|
|
|
def setStretchAllColumns(self, stretch: bool) -> None:
|
|
self._stretch_allowed = stretch
|
|
|
|
def toString(self) -> str:
|
|
return f"TableLayout(rows={len(self._rows)})"
|
|
|
|
|
|
class TableRow(_BaseTableRow):
|
|
"""TableRow — 表格行"""
|
|
|
|
class LayoutParams(Stub):
|
|
"""TableRow.LayoutParams — 行布局参数"""
|
|
|
|
def __init__(self, width: int = MATCH_PARENT, height: int = WRAP_CONTENT):
|
|
super().__init__()
|
|
self.width = width
|
|
self.height = height
|
|
self.column: int = -1
|
|
self.span: int = 1
|
|
|
|
def toString(self) -> str:
|
|
return f"TableRow.LayoutParams(col={self.column}, span={self.span})"
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._children: List[Any] = []
|
|
|
|
def addView(self, child: Any, index: int = -1) -> None:
|
|
if index < 0 or index >= len(self._children):
|
|
self._children.append(child)
|
|
else:
|
|
self._children.insert(index, child)
|
|
|
|
def getChildCount(self) -> int:
|
|
return len(self._children)
|
|
|
|
def getVirtualChildAt(self, index: int) -> Optional[Any]:
|
|
if 0 <= index < len(self._children):
|
|
return self._children[index]
|
|
return None
|
|
|
|
def toString(self) -> str:
|
|
return f"TableRow(children={len(self._children)})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# GridLayout
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class GridLayout(Stub):
|
|
"""GridLayout — 网格布局"""
|
|
|
|
class Alignment(Stub):
|
|
"""GridLayout.Alignment — 对齐方式"""
|
|
|
|
def __init__(self, name: str = ""):
|
|
super().__init__()
|
|
self._name = name
|
|
|
|
def toString(self) -> str:
|
|
return f"Alignment({self._name})"
|
|
|
|
FILL = Alignment("FILL")
|
|
CENTER = Alignment("CENTER")
|
|
LEFT = Alignment("LEFT")
|
|
RIGHT = Alignment("RIGHT")
|
|
TOP = Alignment("TOP")
|
|
BOTTOM = Alignment("BOTTOM")
|
|
BASELINE = Alignment("BASELINE")
|
|
START = Alignment("START")
|
|
END = Alignment("END")
|
|
|
|
ALIGN_BOUNDS = 0
|
|
ALIGN_MARGINS = 1
|
|
|
|
HORIZONTAL = 0
|
|
VERTICAL = 1
|
|
|
|
UNDEFINED = -2147483648
|
|
|
|
class Spec(Stub):
|
|
"""GridLayout.Spec — 网格规格"""
|
|
|
|
def __init__(self, start: int = -2147483648, span: int = 1,
|
|
alignment: Optional["GridLayout.Alignment"] = None):
|
|
super().__init__()
|
|
self.start = start
|
|
self.span = span
|
|
self.alignment = alignment
|
|
|
|
def toString(self) -> str:
|
|
return f"Spec(start={self.start}, span={self.span})"
|
|
|
|
class LayoutParams(Stub):
|
|
"""GridLayout.LayoutParams — 网格布局参数"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.width = WRAP_CONTENT
|
|
self.height = WRAP_CONTENT
|
|
self.columnSpec = GridLayout.Spec(GridLayout.UNDEFINED, 1, GridLayout.FILL)
|
|
self.rowSpec = GridLayout.Spec(GridLayout.UNDEFINED, 1, GridLayout.FILL)
|
|
self.setMargins(0, 0, 0, 0)
|
|
self.leftMargin = 0
|
|
self.topMargin = 0
|
|
self.rightMargin = 0
|
|
self.bottomMargin = 0
|
|
|
|
def setMargins(self, left: int, top: int, right: int, bottom: int) -> None:
|
|
self.leftMargin = left
|
|
self.topMargin = top
|
|
self.rightMargin = right
|
|
self.bottomMargin = bottom
|
|
|
|
def setGravity(self, gravity: int) -> None:
|
|
pass
|
|
|
|
def toString(self) -> str:
|
|
return (f"GridLayout.LayoutParams(col={self.columnSpec}, "
|
|
f"row={self.rowSpec})")
|
|
|
|
@staticmethod
|
|
def spec(start: int = UNDEFINED, span: int = 1,
|
|
alignment: Optional[Alignment] = None) -> Spec:
|
|
return GridLayout.Spec(start, span, alignment)
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._orientation = self.HORIZONTAL
|
|
self._column_count: int = 2
|
|
self._row_count: int = 2
|
|
self._alignment_mode = self.ALIGN_BOUNDS
|
|
self._use_default_margins = False
|
|
self._children: List[Any] = []
|
|
|
|
def setOrientation(self, orientation: int) -> None:
|
|
self._orientation = orientation
|
|
|
|
def getOrientation(self) -> int:
|
|
return self._orientation
|
|
|
|
def setColumnCount(self, count: int) -> None:
|
|
self._column_count = max(0, count)
|
|
|
|
def getColumnCount(self) -> int:
|
|
return self._column_count
|
|
|
|
def setRowCount(self, count: int) -> None:
|
|
self._row_count = max(0, count)
|
|
|
|
def getRowCount(self) -> int:
|
|
return self._row_count
|
|
|
|
def setAlignmentMode(self, mode: int) -> None:
|
|
self._alignment_mode = mode
|
|
|
|
def getAlignmentMode(self) -> int:
|
|
return self._alignment_mode
|
|
|
|
def setUseDefaultMargins(self, use: bool) -> None:
|
|
self._use_default_margins = use
|
|
|
|
def getUseDefaultMargins(self) -> bool:
|
|
return self._use_default_margins
|
|
|
|
def toString(self) -> str:
|
|
return f"GridLayout({self._column_count}x{self._row_count})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# LinearLayout / FrameLayout / RelativeLayout
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class LinearLayout(_BaseLinearLayout):
|
|
"""LinearLayout — 线性布局"""
|
|
|
|
HORIZONTAL = 0
|
|
VERTICAL = 1
|
|
SHOW_DIVIDER_NONE = 0
|
|
SHOW_DIVIDER_BEGINNING = 1
|
|
SHOW_DIVIDER_MIDDLE = 2
|
|
SHOW_DIVIDER_END = 4
|
|
|
|
class LayoutParams(Stub):
|
|
"""LinearLayout.LayoutParams — 线性布局参数"""
|
|
|
|
def __init__(self, width: int = MATCH_PARENT, height: int = WRAP_CONTENT,
|
|
weight: float = 0.0):
|
|
super().__init__()
|
|
self.width = width
|
|
self.height = height
|
|
self.weight = weight
|
|
self.gravity = -1
|
|
self.leftMargin = 0
|
|
self.topMargin = 0
|
|
self.rightMargin = 0
|
|
self.bottomMargin = 0
|
|
|
|
def setMargins(self, left: int, top: int, right: int, bottom: int) -> None:
|
|
self.leftMargin = left
|
|
self.topMargin = top
|
|
self.rightMargin = right
|
|
self.bottomMargin = bottom
|
|
|
|
def toString(self) -> str:
|
|
return (f"LinearLayout.LayoutParams({self.width}x{self.height}, "
|
|
f"weight={self.weight})")
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._orientation = self.HORIZONTAL
|
|
self._weight_sum: float = 0.0
|
|
self._baseline_aligned = True
|
|
self._divider: Optional[Any] = None
|
|
self._divider_padding: int = 0
|
|
self._show_dividers = self.SHOW_DIVIDER_NONE
|
|
self._gravity: int = -1
|
|
|
|
def setOrientation(self, orientation: int) -> None:
|
|
self._orientation = orientation
|
|
|
|
def getOrientation(self) -> int:
|
|
return self._orientation
|
|
|
|
def setWeightSum(self, weight_sum: float) -> None:
|
|
self._weight_sum = weight_sum
|
|
|
|
def getWeightSum(self) -> float:
|
|
return self._weight_sum
|
|
|
|
def setBaselineAligned(self, aligned: bool) -> None:
|
|
self._baseline_aligned = aligned
|
|
|
|
def isBaselineAligned(self) -> bool:
|
|
return self._baseline_aligned
|
|
|
|
def setDividerDrawable(self, divider: Any) -> None:
|
|
self._divider = divider
|
|
|
|
def getDividerDrawable(self) -> Optional[Any]:
|
|
return self._divider
|
|
|
|
def setDividerPadding(self, padding: int) -> None:
|
|
self._divider_padding = padding
|
|
|
|
def getDividerPadding(self) -> int:
|
|
return self._divider_padding
|
|
|
|
def setShowDividers(self, show: int) -> None:
|
|
self._show_dividers = show
|
|
|
|
def getShowDividers(self) -> int:
|
|
return self._show_dividers
|
|
|
|
def setGravity(self, gravity: int) -> None:
|
|
self._gravity = gravity
|
|
|
|
def getGravity(self) -> int:
|
|
return self._gravity
|
|
|
|
def toString(self) -> str:
|
|
return f"LinearLayout(orientation={'H' if self._orientation == 0 else 'V'})"
|
|
|
|
|
|
class FrameLayout(_BaseFrameLayout):
|
|
"""FrameLayout — 帧布局"""
|
|
|
|
class LayoutParams(Stub):
|
|
"""FrameLayout.LayoutParams — 帧布局参数"""
|
|
|
|
UNSPECIFIED_GRAVITY = -1
|
|
|
|
def __init__(self, width: int = MATCH_PARENT, height: int = MATCH_PARENT,
|
|
gravity: int = UNSPECIFIED_GRAVITY):
|
|
super().__init__()
|
|
self.width = width
|
|
self.height = height
|
|
self.gravity = gravity
|
|
self.leftMargin = 0
|
|
self.topMargin = 0
|
|
self.rightMargin = 0
|
|
self.bottomMargin = 0
|
|
|
|
def setMargins(self, left: int, top: int, right: int, bottom: int) -> None:
|
|
self.leftMargin = left
|
|
self.topMargin = top
|
|
self.rightMargin = right
|
|
self.bottomMargin = bottom
|
|
|
|
def toString(self) -> str:
|
|
return (f"FrameLayout.LayoutParams({self.width}x{self.height}, "
|
|
f"gravity={self.gravity})")
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._foreground: Optional[Any] = None
|
|
self._foreground_gravity: int = 0
|
|
self._measure_all_children = False
|
|
|
|
def setForeground(self, drawable: Any) -> None:
|
|
self._foreground = drawable
|
|
|
|
def getForeground(self) -> Optional[Any]:
|
|
return self._foreground
|
|
|
|
def setForegroundGravity(self, gravity: int) -> None:
|
|
self._foreground_gravity = gravity
|
|
|
|
def getForegroundGravity(self) -> int:
|
|
return self._foreground_gravity
|
|
|
|
def setMeasureAllChildren(self, measure: bool) -> None:
|
|
self._measure_all_children = measure
|
|
|
|
def getMeasureAllChildren(self) -> bool:
|
|
return self._measure_all_children
|
|
|
|
def toString(self) -> str:
|
|
return "FrameLayout()"
|
|
|
|
|
|
class RelativeLayout(_BaseRelativeLayout):
|
|
"""RelativeLayout — 相对布局"""
|
|
|
|
TRUE = -1
|
|
FALSE = -2
|
|
|
|
ALIGN_PARENT_LEFT = 1
|
|
ALIGN_PARENT_TOP = 2
|
|
ALIGN_PARENT_RIGHT = 4
|
|
ALIGN_PARENT_BOTTOM = 8
|
|
ALIGN_PARENT_START = 16
|
|
ALIGN_PARENT_END = 32
|
|
CENTER_IN_PARENT = 64
|
|
CENTER_HORIZONTAL = 128
|
|
CENTER_VERTICAL = 256
|
|
ABOVE = 512
|
|
BELOW = 1024
|
|
LEFT_OF = 2048
|
|
RIGHT_OF = 4096
|
|
ALIGN_LEFT = 8192
|
|
ALIGN_TOP = 16384
|
|
ALIGN_RIGHT = 32768
|
|
ALIGN_BOTTOM = 65536
|
|
ALIGN_BASELINE = 131072
|
|
ALIGN_START = 262144
|
|
ALIGN_END = 524288
|
|
START_OF = 1048576
|
|
END_OF = 2097152
|
|
LAYOUT_DIRECTION = 4194304
|
|
|
|
class LayoutParams(Stub):
|
|
"""RelativeLayout.LayoutParams — 相对布局参数"""
|
|
|
|
def __init__(self, width: int = WRAP_CONTENT, height: int = WRAP_CONTENT):
|
|
super().__init__()
|
|
self.width = width
|
|
self.height = height
|
|
self.leftMargin = 0
|
|
self.topMargin = 0
|
|
self.rightMargin = 0
|
|
self.bottomMargin = 0
|
|
self.marginStart = 0
|
|
self.marginEnd = 0
|
|
self._rules: Dict[int, int] = {}
|
|
self._align_with_parent = False
|
|
|
|
def addRule(self, verb: int, subject: int = -1) -> None:
|
|
self._rules[verb] = subject
|
|
|
|
def removeRule(self, verb: int) -> None:
|
|
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 setMargins(self, left: int, top: int, right: int, bottom: int) -> None:
|
|
self.leftMargin = left
|
|
self.topMargin = top
|
|
self.rightMargin = right
|
|
self.bottomMargin = bottom
|
|
|
|
def setMarginStart(self, start: int) -> None:
|
|
self.marginStart = start
|
|
|
|
def setMarginEnd(self, end: int) -> None:
|
|
self.marginEnd = end
|
|
|
|
def toString(self) -> str:
|
|
return (f"RelativeLayout.LayoutParams({self.width}x{self.height}, "
|
|
f"rules={len(self._rules)})")
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._gravity: int = -1
|
|
self._horizontal_gravity: int = -1
|
|
self._vertical_gravity: int = -1
|
|
self._ignore_gravity = False
|
|
|
|
def setGravity(self, gravity: int) -> None:
|
|
self._gravity = gravity
|
|
|
|
def getGravity(self) -> int:
|
|
return self._gravity
|
|
|
|
def setHorizontalGravity(self, gravity: int) -> None:
|
|
self._horizontal_gravity = gravity
|
|
|
|
def setVerticalGravity(self, gravity: int) -> None:
|
|
self._vertical_gravity = gravity
|
|
|
|
def setIgnoreGravity(self, view_id: int) -> None:
|
|
self._ignore_gravity = True
|
|
|
|
def getIgnoreGravity(self) -> int:
|
|
return 0
|
|
|
|
def toString(self) -> str:
|
|
return "RelativeLayout()"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# NumberPicker / SearchView / TabHost / TabWidget
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class NumberPicker(_BaseNumberPicker):
|
|
"""NumberPicker — 数字选择器"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._min_value: int = 0
|
|
self._max_value: int = 100
|
|
self._value: int = 0
|
|
self._wrap_selector_wheel = True
|
|
self._on_value_change: Optional[Callable] = None
|
|
self._on_scroll: Optional[Callable] = None
|
|
self._displayed_values: Optional[List[str]] = None
|
|
self._descendant_focus = False
|
|
|
|
def setMinValue(self, value: int) -> None:
|
|
self._min_value = value
|
|
if self._value < value:
|
|
self._value = value
|
|
|
|
def getMinValue(self) -> int:
|
|
return self._min_value
|
|
|
|
def setMaxValue(self, value: int) -> None:
|
|
self._max_value = value
|
|
if self._value > value:
|
|
self._value = value
|
|
|
|
def getMaxValue(self) -> int:
|
|
return self._max_value
|
|
|
|
def setValue(self, value: int) -> None:
|
|
self._value = max(self._min_value, min(self._max_value, value))
|
|
if self._on_value_change:
|
|
self._on_value_change(self, self._value, self._value)
|
|
|
|
def getValue(self) -> int:
|
|
return self._value
|
|
|
|
def setWrapSelectorWheel(self, wrap: bool) -> None:
|
|
self._wrap_selector_wheel = wrap
|
|
|
|
def getWrapSelectorWheel(self) -> bool:
|
|
return self._wrap_selector_wheel
|
|
|
|
def setDisplayedValues(self, values: Optional[List[str]]) -> None:
|
|
self._displayed_values = values
|
|
|
|
def getDisplayedValues(self) -> Optional[List[str]]:
|
|
return self._displayed_values
|
|
|
|
def setOnValueChangedListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_value_change = listener
|
|
|
|
def setOnScrollListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_scroll = listener
|
|
|
|
def toString(self) -> str:
|
|
return f"NumberPicker(value={self._value}, range=[{self._min_value}-{self._max_value}])"
|
|
|
|
|
|
class SearchView(_BaseSearchView):
|
|
"""SearchView — 搜索视图扩展"""
|
|
|
|
ICONIFIED = 0
|
|
EXPANDED = 1
|
|
COLLAPSED = 2
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._query: str = ""
|
|
self._query_hint: Optional[str] = None
|
|
self._iconified = True
|
|
self._iconified_by_default = True
|
|
self._submit_button_enabled = True
|
|
self._query_refinement_enabled = False
|
|
self._searchable_info: Optional[Any] = None
|
|
self._on_query: Optional[Callable] = None
|
|
self._on_query_text: Optional[Callable] = None
|
|
self._on_close: Optional[Callable] = None
|
|
self._on_suggestion: Optional[Callable] = None
|
|
|
|
def setQuery(self, query: str, submit: bool = False) -> None:
|
|
self._query = query
|
|
if submit and self._on_query:
|
|
self._on_query(query)
|
|
|
|
def getQuery(self) -> str:
|
|
return self._query
|
|
|
|
def setQueryHint(self, hint: Optional[str]) -> None:
|
|
self._query_hint = hint
|
|
|
|
def getQueryHint(self) -> Optional[str]:
|
|
return self._query_hint
|
|
|
|
def setIconified(self, iconified: bool) -> None:
|
|
self._iconified = iconified
|
|
|
|
def isIconified(self) -> bool:
|
|
return self._iconified
|
|
|
|
def setIconifiedByDefault(self, iconified: bool) -> None:
|
|
self._iconified_by_default = iconified
|
|
|
|
def isIconifiedByDefault(self) -> bool:
|
|
return self._iconified_by_default
|
|
|
|
def setSubmitButtonEnabled(self, enabled: bool) -> None:
|
|
self._submit_button_enabled = enabled
|
|
|
|
def isSubmitButtonEnabled(self) -> bool:
|
|
return self._submit_button_enabled
|
|
|
|
def setQueryRefinementEnabled(self, enabled: bool) -> None:
|
|
self._query_refinement_enabled = enabled
|
|
|
|
def isQueryRefinementEnabled(self) -> bool:
|
|
return self._query_refinement_enabled
|
|
|
|
def setOnQueryTextListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_query_text = listener
|
|
|
|
def setOnCloseListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_close = listener
|
|
|
|
def setOnSuggestionListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_suggestion = listener
|
|
|
|
def setSearchableInfo(self, info: Any) -> None:
|
|
self._searchable_info = info
|
|
|
|
def onActionViewExpanded(self) -> None:
|
|
self._iconified = False
|
|
|
|
def onActionViewCollapsed(self) -> None:
|
|
self._iconified = True
|
|
|
|
def toString(self) -> str:
|
|
return f"SearchView(query={self._query})"
|
|
|
|
|
|
class TabHost(_BaseTabHost):
|
|
"""TabHost — 标签宿主"""
|
|
|
|
class TabSpec(Stub):
|
|
"""TabHost.TabSpec — 标签规格"""
|
|
|
|
def __init__(self, tag: str = ""):
|
|
super().__init__()
|
|
self._tag = tag
|
|
self._indicator: Optional[Any] = None
|
|
self._content: Optional[Any] = 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 getTag(self) -> str:
|
|
return self._tag
|
|
|
|
def toString(self) -> str:
|
|
return f"TabSpec(tag={self._tag})"
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._tabs: List[TabHost.TabSpec] = []
|
|
self._current_tab: int = 0
|
|
|
|
def newTabSpec(self, tag: str) -> TabSpec:
|
|
return TabHost.TabSpec(tag)
|
|
|
|
def addTab(self, spec: TabSpec) -> None:
|
|
self._tabs.append(spec)
|
|
|
|
def setCurrentTab(self, index: int) -> None:
|
|
if 0 <= index < len(self._tabs):
|
|
self._current_tab = index
|
|
|
|
def setCurrentTabByTag(self, tag: str) -> None:
|
|
for i, spec in enumerate(self._tabs):
|
|
if spec.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) -> Optional["TabWidget"]:
|
|
return None
|
|
|
|
def getTabContentView(self) -> Optional[Any]:
|
|
return None
|
|
|
|
def getTabCount(self) -> int:
|
|
return len(self._tabs)
|
|
|
|
def toString(self) -> str:
|
|
return f"TabHost(tabs={len(self._tabs)}, current={self._current_tab})"
|
|
|
|
|
|
class TabWidget(_BaseTabWidget):
|
|
"""TabWidget — 标签小部件"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._tabs: List[Any] = []
|
|
self._current_tab: int = 0
|
|
self._strip_enabled = False
|
|
self._strip_left: int = 0
|
|
self._strip_top: int = 0
|
|
self._strip_right: int = 0
|
|
self._strip_bottom: int = 0
|
|
|
|
def setCurrentTab(self, index: int) -> None:
|
|
self._current_tab = max(0, min(index, len(self._tabs) - 1))
|
|
|
|
def getCurrentTab(self) -> int:
|
|
return self._current_tab
|
|
|
|
def setStripEnabled(self, enabled: bool) -> None:
|
|
self._strip_enabled = enabled
|
|
|
|
def isStripEnabled(self) -> bool:
|
|
return self._strip_enabled
|
|
|
|
def getTabCount(self) -> int:
|
|
return len(self._tabs)
|
|
|
|
def toString(self) -> str:
|
|
return f"TabWidget(tabs={len(self._tabs)})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# TextSwitcher / ViewSwitcher / ViewAnimator / ImageSwitcher
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class ViewAnimator(_BaseViewAnimator):
|
|
"""ViewAnimator — 视图动画器"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._children: List[Any] = []
|
|
self._displayed_child: int = 0
|
|
self._in_animation: Optional[Any] = None
|
|
self._out_animation: Optional[Any] = None
|
|
self._animate_first_view = False
|
|
|
|
def addView(self, child: Any) -> None:
|
|
self._children.append(child)
|
|
|
|
def removeView(self, child: Any) -> None:
|
|
if child in self._children:
|
|
self._children.remove(child)
|
|
|
|
def removeAllViews(self) -> None:
|
|
self._children.clear()
|
|
|
|
def getChildCount(self) -> int:
|
|
return len(self._children)
|
|
|
|
def getCurrentView(self) -> Optional[Any]:
|
|
if 0 <= self._displayed_child < len(self._children):
|
|
return self._children[self._displayed_child]
|
|
return None
|
|
|
|
def getDisplayedChild(self) -> int:
|
|
return self._displayed_child
|
|
|
|
def setDisplayedChild(self, index: int) -> None:
|
|
if 0 <= index < len(self._children):
|
|
self._displayed_child = index
|
|
|
|
def showNext(self) -> None:
|
|
if self._children:
|
|
self._displayed_child = (self._displayed_child + 1) % len(self._children)
|
|
|
|
def showPrevious(self) -> None:
|
|
if self._children:
|
|
self._displayed_child = (self._displayed_child - 1) % len(self._children)
|
|
|
|
def setInAnimation(self, animation: Any) -> None:
|
|
self._in_animation = animation
|
|
|
|
def getInAnimation(self) -> Optional[Any]:
|
|
return self._in_animation
|
|
|
|
def setOutAnimation(self, animation: Any) -> None:
|
|
self._out_animation = animation
|
|
|
|
def getOutAnimation(self) -> Optional[Any]:
|
|
return self._out_animation
|
|
|
|
def setAnimateFirstView(self, animate: bool) -> None:
|
|
self._animate_first_view = animate
|
|
|
|
def getAnimateFirstView(self) -> bool:
|
|
return self._animate_first_view
|
|
|
|
def toString(self) -> str:
|
|
return f"ViewAnimator(children={len(self._children)}, displayed={self._displayed_child})"
|
|
|
|
|
|
class ViewSwitcher(_BaseViewSwitcher, ViewAnimator):
|
|
"""ViewSwitcher — 视图切换器"""
|
|
|
|
class ViewFactory(Stub):
|
|
"""ViewSwitcher.ViewFactory — 视图工厂"""
|
|
def makeView(self) -> Any:
|
|
return None
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__(context)
|
|
ViewAnimator.__init__(self, context)
|
|
self._factory: Optional[ViewSwitcher.ViewFactory] = None
|
|
|
|
def setFactory(self, factory: ViewFactory) -> None:
|
|
self._factory = factory
|
|
|
|
def getNextView(self) -> Optional[Any]:
|
|
next_idx = (self.getDisplayedChild() + 1) % max(1, self.getChildCount())
|
|
if 0 <= next_idx < self.getChildCount():
|
|
return None
|
|
return None
|
|
|
|
def reset(self) -> None:
|
|
self.removeAllViews()
|
|
|
|
def toString(self) -> str:
|
|
return f"ViewSwitcher()"
|
|
|
|
|
|
class TextSwitcher(_BaseTextSwitcher, ViewSwitcher):
|
|
"""TextSwitcher — 文本切换器"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__(context)
|
|
ViewSwitcher.__init__(self, context)
|
|
self._factory = None
|
|
|
|
def setCurrentText(self, text: str) -> None:
|
|
pass
|
|
|
|
def getCurrentText(self) -> str:
|
|
return ""
|
|
|
|
def toString(self) -> str:
|
|
return "TextSwitcher()"
|
|
|
|
|
|
class ImageSwitcher(_BaseImageSwitcher, ViewSwitcher):
|
|
"""ImageSwitcher — 图片切换器"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__(context)
|
|
ViewSwitcher.__init__(self, context)
|
|
|
|
def setImageResource(self, res_id: int) -> None:
|
|
pass
|
|
|
|
def setImageDrawable(self, drawable: Any) -> None:
|
|
pass
|
|
|
|
def setImageURI(self, uri: Any) -> None:
|
|
pass
|
|
|
|
def toString(self) -> str:
|
|
return "ImageSwitcher()"
|
|
|
|
|
|
class ImageButton(_BaseImageButton):
|
|
"""ImageButton — 图片按钮"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._adjust_view_bounds = False
|
|
self._scale_type: Optional[Any] = None
|
|
|
|
def setAdjustViewBounds(self, adjust: bool) -> None:
|
|
self._adjust_view_bounds = adjust
|
|
|
|
def getAdjustViewBounds(self) -> bool:
|
|
return self._adjust_view_bounds
|
|
|
|
def setScaleType(self, scale_type: Any) -> None:
|
|
self._scale_type = scale_type
|
|
|
|
def getScaleType(self) -> Optional[Any]:
|
|
return self._scale_type
|
|
|
|
def toString(self) -> str:
|
|
return "ImageButton()"
|
|
|
|
|
|
class ZoomButton(ImageButton):
|
|
"""ZoomButton — 缩放按钮"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__(context)
|
|
self._zoom_in = False
|
|
self._zoom_speed: int = 300
|
|
self._on_click: Optional[Callable] = None
|
|
|
|
def setZoomIn(self, zoom_in: bool) -> None:
|
|
self._zoom_in = zoom_in
|
|
|
|
def isZoomIn(self) -> bool:
|
|
return self._zoom_in
|
|
|
|
def setZoomSpeed(self, speed: int) -> None:
|
|
self._zoom_speed = speed
|
|
|
|
def getZoomSpeed(self) -> int:
|
|
return self._zoom_speed
|
|
|
|
def setOnZoomListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_click = listener
|
|
|
|
def toString(self) -> str:
|
|
return f"ZoomButton(zoomIn={self._zoom_in})"
|
|
|
|
|
|
class ZoomControls(_BaseZoomControls):
|
|
"""ZoomControls — 缩放控件"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._zoom_in_enabled = True
|
|
self._zoom_out_enabled = True
|
|
self._on_zoom_in: Optional[Callable] = None
|
|
self._on_zoom_out: Optional[Callable] = None
|
|
self._show_zoom_controls = True
|
|
|
|
def setOnZoomInClickListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_zoom_in = listener
|
|
|
|
def setOnZoomOutClickListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_zoom_out = listener
|
|
|
|
def setZoomInEnabled(self, enabled: bool) -> None:
|
|
self._zoom_in_enabled = enabled
|
|
|
|
def setZoomOutEnabled(self, enabled: bool) -> None:
|
|
self._zoom_out_enabled = enabled
|
|
|
|
def show(self) -> None:
|
|
self._show_zoom_controls = True
|
|
|
|
def hide(self) -> None:
|
|
self._show_zoom_controls = False
|
|
|
|
def setIsZoomInEnabled(self, enabled: bool) -> None:
|
|
self._zoom_in_enabled = enabled
|
|
|
|
def setIsZoomOutEnabled(self, enabled: bool) -> None:
|
|
self._zoom_out_enabled = enabled
|
|
|
|
def hasFocus(self) -> bool:
|
|
return False
|
|
|
|
def toString(self) -> str:
|
|
return "ZoomControls()"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# DigitalClock / TextClock / AnalogClock
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class DigitalClock(_BaseDigitalClock):
|
|
"""DigitalClock — 数字时钟"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._format: str = "%H:%M:%S"
|
|
self._on_tick: Optional[Callable] = None
|
|
|
|
def setFormat(self, format_str: str) -> None:
|
|
self._format = format_str
|
|
|
|
def getFormat(self) -> str:
|
|
return self._format
|
|
|
|
def getTime(self) -> str:
|
|
import datetime
|
|
return datetime.datetime.now().strftime(self._format)
|
|
|
|
def setOnTickListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_tick = listener
|
|
|
|
def toString(self) -> str:
|
|
return f"DigitalClock(time={self.getTime()})"
|
|
|
|
|
|
class TextClock(Stub):
|
|
"""TextClock — 文本时钟"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._format_12_hour: str = "hh:mm:ss a"
|
|
self._format_24_hour: str = "HH:mm:ss"
|
|
self._time_zone: Optional[str] = None
|
|
|
|
def setFormat12Hour(self, format_str: str) -> None:
|
|
self._format_12_hour = format_str
|
|
|
|
def getFormat12Hour(self) -> str:
|
|
return self._format_12_hour
|
|
|
|
def setFormat24Hour(self, format_str: str) -> None:
|
|
self._format_24_hour = format_str
|
|
|
|
def getFormat24Hour(self) -> str:
|
|
return self._format_24_hour
|
|
|
|
def setTimeZone(self, tz: str) -> None:
|
|
self._time_zone = tz
|
|
|
|
def getTimeZone(self) -> Optional[str]:
|
|
return self._time_zone
|
|
|
|
def is24HourModeEnabled(self) -> bool:
|
|
return True
|
|
|
|
def getText(self) -> str:
|
|
import datetime
|
|
if self._time_zone:
|
|
import pytz
|
|
now = datetime.datetime.now(pytz.timezone(self._time_zone))
|
|
else:
|
|
now = datetime.datetime.now()
|
|
return now.strftime(self._format_24_hour)
|
|
|
|
def toString(self) -> str:
|
|
return f"TextClock(time={self.getText()})"
|
|
|
|
|
|
class AnalogClock(_BaseAnalogClock):
|
|
"""AnalogClock — 模拟时钟"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._dial: Optional[Any] = None
|
|
self._hour_hand: Optional[Any] = None
|
|
self._minute_hand: Optional[Any] = None
|
|
self._second_hand: Optional[Any] = None
|
|
|
|
def toString(self) -> str:
|
|
return "AnalogClock()"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# QuickContactBadge
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class QuickContactBadge(Stub):
|
|
"""QuickContactBadge — 快速联系人徽章"""
|
|
|
|
MODE_SMALL = 0
|
|
MODE_MEDIUM = 1
|
|
MODE_LARGE = 2
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._contact_uri: Optional[str] = None
|
|
self._mode = self.MODE_MEDIUM
|
|
self._on_click: Optional[Callable] = None
|
|
|
|
def assignContactUri(self, uri: str) -> None:
|
|
self._contact_uri = uri
|
|
|
|
def assignContactFromEmail(self, email: str, lazy: bool = False) -> None:
|
|
self._contact_uri = f"mailto:{email}"
|
|
|
|
def assignContactFromPhone(self, phone: str, lazy: bool = False) -> None:
|
|
self._contact_uri = f"tel:{phone}"
|
|
|
|
def setMode(self, mode: int) -> None:
|
|
self._mode = mode
|
|
|
|
def getMode(self) -> int:
|
|
return self._mode
|
|
|
|
def setOnClickListener(self, listener: Optional[Callable]) -> None:
|
|
self._on_click = listener
|
|
|
|
def toString(self) -> str:
|
|
return f"QuickContactBadge(contact={self._contact_uri})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# 各种 Adapter 实现
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class BaseAdapter(_BaseBaseAdapter, Adapter):
|
|
"""BaseAdapter — 基础适配器"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._data_set_observers: List[Callable] = []
|
|
|
|
def registerDataSetObserver(self, observer: Callable) -> None:
|
|
if observer not in self._data_set_observers:
|
|
self._data_set_observers.append(observer)
|
|
|
|
def unregisterDataSetObserver(self, observer: Callable) -> None:
|
|
if observer in self._data_set_observers:
|
|
self._data_set_observers.remove(observer)
|
|
|
|
def notifyDataSetChanged(self) -> None:
|
|
for obs in list(self._data_set_observers):
|
|
obs()
|
|
|
|
def notifyDataSetInvalidated(self) -> None:
|
|
self.notifyDataSetChanged()
|
|
|
|
def areAllItemsEnabled(self) -> bool:
|
|
return True
|
|
|
|
def isEnabled(self, position: int) -> bool:
|
|
return True
|
|
|
|
def getItemViewType(self, position: int) -> int:
|
|
return Adapter.IGNORE_ITEM_VIEW_TYPE
|
|
|
|
def getViewTypeCount(self) -> int:
|
|
return 1
|
|
|
|
def hasStableIds(self) -> bool:
|
|
return False
|
|
|
|
def isEmpty(self) -> bool:
|
|
return self.getCount() == 0
|
|
|
|
|
|
class ArrayAdapter(_BaseArrayAdapter, BaseAdapter):
|
|
"""ArrayAdapter — 数组适配器"""
|
|
|
|
def __init__(self, context: Any = None, resource: int = 0,
|
|
objects: Optional[List[Any]] = None):
|
|
BaseAdapter.__init__(self)
|
|
self._context = context
|
|
self._resource = resource
|
|
self._drop_down_resource: int = resource
|
|
self._objects: List[Any] = list(objects) if objects else []
|
|
self._lock = threading.Lock()
|
|
|
|
def add(self, obj: Any) -> None:
|
|
with self._lock:
|
|
self._objects.append(obj)
|
|
self.notifyDataSetChanged()
|
|
|
|
def addAll(self, collection: List[Any]) -> None:
|
|
with self._lock:
|
|
self._objects.extend(collection)
|
|
self.notifyDataSetChanged()
|
|
|
|
def insert(self, obj: Any, index: int) -> None:
|
|
with self._lock:
|
|
self._objects.insert(index, obj)
|
|
self.notifyDataSetChanged()
|
|
|
|
def remove(self, obj: Any) -> None:
|
|
with self._lock:
|
|
if obj in self._objects:
|
|
self._objects.remove(obj)
|
|
self.notifyDataSetChanged()
|
|
|
|
def clear(self) -> None:
|
|
with self._lock:
|
|
self._objects.clear()
|
|
self.notifyDataSetChanged()
|
|
|
|
def sort(self, comparator: Callable) -> None:
|
|
with self._lock:
|
|
self._objects.sort(key=comparator)
|
|
self.notifyDataSetChanged()
|
|
|
|
def getCount(self) -> int:
|
|
return len(self._objects)
|
|
|
|
def getItem(self, position: int) -> Optional[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 setDropDownViewResource(self, resource: int) -> None:
|
|
self._drop_down_resource = resource
|
|
|
|
def getDropDownViewResource(self) -> int:
|
|
return self._drop_down_resource
|
|
|
|
def toString(self) -> str:
|
|
return f"ArrayAdapter(items={len(self._objects)})"
|
|
|
|
|
|
class SimpleAdapter(_BaseSimpleAdapter, BaseAdapter):
|
|
"""SimpleAdapter — 简单适配器"""
|
|
|
|
def __init__(self, context: Any = None,
|
|
data: Optional[List[Dict[str, Any]]] = None,
|
|
resource: int = 0,
|
|
from_keys: Optional[List[str]] = None,
|
|
to_ids: Optional[List[int]] = None):
|
|
BaseAdapter.__init__(self)
|
|
self._context = context
|
|
self._data: List[Dict[str, Any]] = data or []
|
|
self._resource = resource
|
|
self._from_keys: List[str] = list(from_keys) if from_keys else []
|
|
self._to_ids: List[int] = list(to_ids) if to_ids else []
|
|
self._drop_down_resource: int = resource
|
|
self._view_binder: Optional[Callable] = None
|
|
|
|
def getCount(self) -> int:
|
|
return len(self._data)
|
|
|
|
def getItem(self, position: int) -> Optional[Dict[str, Any]]:
|
|
if 0 <= position < len(self._data):
|
|
return self._data[position]
|
|
return None
|
|
|
|
def getViewBinder(self) -> Optional[Callable]:
|
|
return self._view_binder
|
|
|
|
def setViewBinder(self, binder: Optional[Callable]) -> None:
|
|
self._view_binder = binder
|
|
|
|
def setDropDownViewResource(self, resource: int) -> None:
|
|
self._drop_down_resource = resource
|
|
|
|
def getDropDownViewResource(self) -> int:
|
|
return self._drop_down_resource
|
|
|
|
def toString(self) -> str:
|
|
return f"SimpleAdapter(items={len(self._data)})"
|
|
|
|
|
|
class CursorAdapter(_BaseCursorAdapter, BaseAdapter):
|
|
"""CursorAdapter — 游标适配器"""
|
|
|
|
FLAG_AUTO_REQUERY = 1
|
|
FLAG_REGISTER_CONTENT_OBSERVER = 2
|
|
|
|
def __init__(self, context: Any = None, cursor: Any = None,
|
|
flags: int = FLAG_AUTO_REQUERY):
|
|
BaseAdapter.__init__(self)
|
|
self._context = context
|
|
self._cursor = cursor
|
|
self._flags = flags
|
|
self._row_id_column: str = "_id"
|
|
self._lock = threading.Lock()
|
|
|
|
def getCursor(self) -> Optional[Any]:
|
|
return self._cursor
|
|
|
|
def setCursor(self, cursor: Any) -> None:
|
|
self._cursor = cursor
|
|
self.notifyDataSetChanged()
|
|
|
|
def getCount(self) -> int:
|
|
return 0
|
|
|
|
def getItem(self, position: int) -> Optional[Any]:
|
|
return None
|
|
|
|
def getItemId(self, position: int) -> int:
|
|
return position
|
|
|
|
def changeCursor(self, cursor: Any) -> Optional[Any]:
|
|
old = self._cursor
|
|
self._cursor = cursor
|
|
self.notifyDataSetChanged()
|
|
return old
|
|
|
|
def swapCursor(self, cursor: Any) -> Optional[Any]:
|
|
old = self._cursor
|
|
self._cursor = cursor
|
|
self.notifyDataSetChanged()
|
|
return old
|
|
|
|
def toString(self) -> str:
|
|
return f"CursorAdapter(cursor={'yes' if self._cursor else 'no'})"
|
|
|
|
|
|
class ResourceCursorAdapter(CursorAdapter):
|
|
"""ResourceCursorAdapter — 资源游标适配器"""
|
|
|
|
def __init__(self, context: Any = None, layout: int = 0,
|
|
cursor: Any = None, flags: int = CursorAdapter.FLAG_AUTO_REQUERY):
|
|
super().__init__(context, cursor, flags)
|
|
self._layout = layout
|
|
self._drop_down_layout: int = layout
|
|
|
|
def setDropDownViewResource(self, resource: int) -> None:
|
|
self._drop_down_layout = resource
|
|
|
|
def getDropDownViewResource(self) -> int:
|
|
return self._drop_down_layout
|
|
|
|
def toString(self) -> str:
|
|
return "ResourceCursorAdapter()"
|
|
|
|
|
|
class SimpleCursorAdapter(ResourceCursorAdapter):
|
|
"""SimpleCursorAdapter — 简单游标适配器"""
|
|
|
|
def __init__(self, context: Any = None, layout: int = 0,
|
|
cursor: Any = None, from_columns: Optional[List[str]] = None,
|
|
to_views: Optional[List[int]] = None,
|
|
flags: int = CursorAdapter.FLAG_AUTO_REQUERY):
|
|
super().__init__(context, layout, cursor, flags)
|
|
self._from_columns: List[str] = list(from_columns) if from_columns else []
|
|
self._to_views: List[int] = list(to_views) if to_views else []
|
|
self._view_binder: Optional[Callable] = None
|
|
self._cursor_to_column_map: Optional[Dict[str, int]] = None
|
|
|
|
def getViewBinder(self) -> Optional[Callable]:
|
|
return self._view_binder
|
|
|
|
def setViewBinder(self, binder: Optional[Callable]) -> None:
|
|
self._view_binder = binder
|
|
|
|
def setCursorToStringConverter(self, converter: Optional[Callable]) -> None:
|
|
pass
|
|
|
|
def toString(self) -> str:
|
|
return "SimpleCursorAdapter()"
|
|
|
|
|
|
class HeaderViewListAdapter(Stub):
|
|
"""HeaderViewListAdapter — 头部视图列表适配器包装"""
|
|
|
|
def __init__(self, wrapped_adapter: Optional[Adapter] = None):
|
|
super().__init__()
|
|
self._wrapped: Optional[Adapter] = wrapped_adapter
|
|
self._headers: List[Any] = []
|
|
self._footers: List[Any] = []
|
|
|
|
def addHeaderView(self, view: Any, data: Any = None, is_selectable: bool = True) -> None:
|
|
self._headers.append(view)
|
|
|
|
def addFooterView(self, view: Any, data: Any = None, is_selectable: bool = True) -> None:
|
|
self._footers.append(view)
|
|
|
|
def removeHeaderView(self, view: Any) -> bool:
|
|
if view in self._headers:
|
|
self._headers.remove(view)
|
|
return True
|
|
return False
|
|
|
|
def removeFooterView(self, view: Any) -> bool:
|
|
if view in self._footers:
|
|
self._footers.remove(view)
|
|
return True
|
|
return False
|
|
|
|
def getCount(self) -> int:
|
|
base = self._wrapped.getCount() if self._wrapped else 0
|
|
return base + len(self._headers) + len(self._footers)
|
|
|
|
def getWrappedAdapter(self) -> Optional[Adapter]:
|
|
return self._wrapped
|
|
|
|
def getHeadersCount(self) -> int:
|
|
return len(self._headers)
|
|
|
|
def getFootersCount(self) -> int:
|
|
return len(self._footers)
|
|
|
|
def isEmpty(self) -> bool:
|
|
return self.getCount() == 0
|
|
|
|
def toString(self) -> str:
|
|
return (f"HeaderViewListAdapter(headers={len(self._headers)}, "
|
|
f"footers={len(self._footers)})")
|
|
|
|
|
|
class WrapperListAdapter(Adapter):
|
|
"""WrapperListAdapter — 包装列表适配器"""
|
|
|
|
def __init__(self, wrapped: Optional[Adapter] = None):
|
|
super().__init__()
|
|
self._wrapped = wrapped
|
|
|
|
def getWrappedAdapter(self) -> Optional[Adapter]:
|
|
return self._wrapped
|
|
|
|
def getCount(self) -> int:
|
|
return self._wrapped.getCount() if self._wrapped else 0
|
|
|
|
def getItem(self, position: int) -> Optional[Any]:
|
|
return self._wrapped.getItem(position) if self._wrapped else None
|
|
|
|
def getItemId(self, position: int) -> int:
|
|
return self._wrapped.getItemId(position) if self._wrapped else position
|
|
|
|
def getView(self, position: int, convert_view: Optional[Any],
|
|
parent: Any) -> Optional[Any]:
|
|
return self._wrapped.getView(position, convert_view, parent) if self._wrapped else None
|
|
|
|
def toString(self) -> str:
|
|
return "WrapperListAdapter()" |