import json import uiautomator2 as u2 class UiElement(object): def __init__(self, d: u2.Device, xpath: str = None, description: str = None, debug: bool = True, point: dict = None) -> None: x, y = 0, 0 self.info = {} self.d = d self.debug = debug if point is not None: self.info['point'] = point self.info['xpath'] = xpath self.info['description'] = description return if xpath is not None: self.info['xpath'] = xpath el = d.xpath(xpath) x, y = el.center() elif description is not None: self.info['description'] = description el = d(description=description) x, y = el.center() self.info['point'] = {'x': x, 'y': y} def log(self, *args, sep=' ', end='\n', file=None): if self.debug: print(args, sep=sep, end=end, file=file) def click(self): """ 点击 """ point = self.info['point'] self.log("点击元素:", self.info) self.d.shell(f"input tap {point['x']} {point['y']}") def text(self, text: str = None): """ 设置文本框内容 text 文本内容 """ point = self.info['point'] self.log("点击元素:", self.info) self.d.shell(f"input tap {point['x']} {point['y']}") self.log("设置文本:", text) self.d.shell(f"input text '{text}'") # 输入文本内容 def clear(self, length: int = 10): """ 清空文本框 """ point = self.info['point'] self.log("点击元素:", self.info) self.d.shell(f"input tap {point['x'] + length} {point['y']}") self.log("清空文本框:") # 模拟删除键输入 # self.d.shell(f"for i in {{1..{20}}}; do input keyevent KEYCODE_CLEAR; done") # 20 次删除键事件 for _ in range(length): self.d.shell("input keyevent 67") class UiFactory(object): """ 这个类对 u2.connect 的几个方法做了封装,主要解决耗时问题。 并且做缓存。不重复定位。 可以使用 load_point 方法 加载 xxx_point.json中,已经配置好的坐标。 """ _cache = {} def __init__(self, serial: str): """ :param serial: 设备序列号。 例如 127.0.0.1:6555 可以通过 `adb devices` 获取 """ self.d = u2.connect(serial) pass def xpath(self, alisa: str, xpath: str = None): """ 通过 xpath获取元素 alisa 别名 xpath 如果别名没有获取缓存, 会通过xpath 重新定位,并且缓存 """ if alisa in self._cache: return self._cache[alisa] el = UiElement(self.d, xpath=xpath) self._cache[alisa] = el return el def desc(self, alisa: str, desc: str = None): """ 通过文本获取元素 alisa 别名 desc 如果别名没有获取缓存, 会通过description 重新定位,并且缓存 """ if alisa in self._cache: return self._cache[alisa] el = UiElement(self.d, description=desc) self._cache[alisa] = el return el def load_point(self, point_json: str): __tmp__ = {} with open(point_json, "r", encoding="utf-8") as f: __tmp__ = json.load(f) for k, v in __tmp__.items(): if 'xpath' in v: self._cache[k] = UiElement(self.d, xpath=v['xpath'], point=v['point']) elif 'description' in v: self._cache[k] = UiElement(self.d, description=v['description'], point=v['point']) def save_point(self, point_json: str): """ 保存坐标 """ __tmp__ = {} for k, v in self._cache.items(): __tmp__[k] = v.info with open(point_json, "w", encoding="utf-8") as f: json.dump(__tmp__, f, ensure_ascii=False, indent=4) def check_app_running(self, appid: str): # 获取所有运行的APP running_apps = self.d.app_list_running() print("安装列表:", running_apps) # 如果app没有启动, 启动APP if appid not in running_apps: print(f"{appid} 重新启动") self.d.app_start(appid) return True return False