UiAutomator2.0遍历控件

本文对比UiAutomator1.0与2.0,详细讲解了使用UiAutomator2.0遍历View树的方法。通过示例代码展示了如何利用BySelector查找控件,使用UiObject2获取子控件列表并进行遍历,同时介绍了2.0版本新增的能力和优势。

使用过UiAutomator1.0的人应该有非常痛的痛点,那就是在遍历View树时非常麻烦或者说做不到,百度上找到的基本上是这么用的:

UiScrollable list = new UiScrollable(new UiSelector().resourceId("com.xxx.xxx:id/ni"));
for (int i = 0; i < list.getChildCount(); i++) {
    UiObject index0Item = list.getChild(new UiSelector().resourceId("com.xxx.xxx:id/z_").instance(i));
    index0Item.click();
}

一般是用resourceId来查找子类,如果只是用className或者instance通常找不到或者找到的并不是这个控件的子控件,如果想要在没有控件id或者统一的类名从一个控件直接获取子控件列表来遍历是做不到的,比如一个控件的子控件有FrameLayout和LinearLayout想得到children再一个一个遍历是不行的,那怎么办?这时就要使用UiAutomator2.0了,这和1.0有什么区别呢?主要是api增多了,能力也增强了,这里就讲一下如何使用2.0来遍历view树。
示例代码:

int height = 0;
//使用By创建BySelector,并用findObject返回UiObject2
UiObject2 list = mDevice.findObject(By.clazz("android.support.v7.widget.RecyclerView"));
//list.getChildren()得到子控件列表的UiObject2
for (UiObject2 child : list.getChildren()) {
    height += child.getVisibleBounds().height();
    //
    if (!child.getClassName().equals("android.widget.LinearLayout")) {
        continue;
    }
    child.click();
}
Rect r = list.getVisibleBounds();
float percent = height / (float)r.height();
//向下翻页
list.scroll(Direction.DOWN, percent);

从代码可以看出,

  • 2.0是使用BySelector做选择器,使用上并1.0更方便和简洁,而且还有更多的能力,比如可以指定查找多少层等
  • 查找控件的结果是UiObject2
  • 子控件列表可以直接使用getChildren得到

使用上就是如此的简单!所以以后都用UiAutomator2.0吧〜
不过目前也看到个小缺点,比如UiScrollable可以用scrollFarward()直接滚动到下页,2.0需要自己计算移动比例再调scroll方法来做翻页。

C:\own\app\python_code\.venv\Scripts\python.exe C:\own\app\python_code\work\IBL_click_all\click_all.py Traceback (most recent call last): File "C:\own\app\python_code\work\IBL_click_all\click_all.py", line 19, in <module> driver = webdriver.Remote('http://localhost:4723', desired_caps) File "C:\own\app\python_code\.venv\Lib\site-packages\appium\webdriver\webdriver.py", line 249, in __init__ super().__init__( ~~~~~~~~~~~~~~~~^ command_executor=command_executor, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...<3 lines>... client_config=client_config, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "C:\own\app\python_code\.venv\Lib\site-packages\selenium\webdriver\remote\webdriver.py", line 231, in __init__ raise TypeError( "missing 1 required keyword-only argument: 'options' (instance of driver `options.Options` class)" ) TypeError: missing 1 required keyword-only argument: 'options' (instance of driver `options.Options` class) Process finished with exit code 1 from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time # 设置 Desired Capabilities desired_caps = { "platformName": "Android", "deviceName": "emulator-5554", # 使用 adb devices 查看设备名 "automationName": "UiAutomator2", "noReset": False, # 不重置应用状态 "fullReset": False, "autoGrantPermissions": True, "newCommandTimeout": 600 } # 连接到 Appium Server driver = webdriver.Remote('http://localhost:4723', desired_caps) # 存储已点击元素的关键信息(防止重复) clicked_elements = set() def get_element_signature(element): """生成一个元素的唯一标识符(用于去重)""" signature_parts = [ element.get_attribute("resourceId"), element.get_attribute("text"), element.get_attribute("className"), element.get_attribute("contentDesc") ] # 过滤 None,并拼接成字符串 return "|".join([part for part in signature_parts if part]) def is_interactable(element): """判断元素是否可交互(可点击)""" clickable = element.get_attribute("clickable") == "true" enabled = element.get_attribute("enabled") == "true" focusable = element.get_attribute("focusable") == "true" scrollable = element.get_attribute("scrollable") == "true" # 只要满足基本交互条件之一即可 return (clickable or focusable) and enabled and not scrollable def click_all_elements(): global clicked_elements max_iterations = 200 # 防止无限循环 iteration_count = 0 while iteration_count < max_iterations: print(f"\n[第 {iteration_count + 1} 轮] 开始扫描当前页面...") try: # 获取当前页面所有控件 elements = driver.find_elements(AppiumBy.XPATH, "//*") new_click_found = False for elem in elements: try: # 判断是否为可交互元素 if not is_interactable(elem): continue # 生成唯一签名 sig = get_element_signature(elem) if not sig or sig in clicked_elements: continue # 记录并点击 print(f"👉 点击元素: {sig}") elem.click() clicked_elements.add(sig) new_click_found = True # 给页面跳转留出时间 time.sleep(2) # 跳出本次循环,重新获取新页面的元素 break # 每次只点一个,然后刷新页面 except Exception as e: # 某些元素可能不可见或已失效,跳过 continue # 如果本轮没有发现新的可点击元素,则退出 if not new_click_found: print("✅ 所有可点击元素均已处理完毕,结束遍历。") break iteration_count += 1 except Exception as e: print(f"⚠️ 页面解析异常: {e}") time.sleep(2) continue print(f"🔚 自动化完成,共执行 {iteration_count} 次操作。") # 执行主任务 try: click_all_elements() finally: driver.quit() # 关闭会话
10-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值