查找元素
如何阅读界面转储,选择持续有效的选择器,处理重复元素、列表、图标、网页和没有元素的界面,以及排查匹配不到的选择器。
本页为译文。如与英文版不一致,以英文版为准。 English
Droidline 脚本的大部分工作,是告诉它要操作哪一个元素。本指南介绍如何像 Droidline 那样查看界面,以及如何选择在应用更新或手机语言改变之后仍然有效的选择器。
先看再写
在手机上打开要操作的界面,然后保存 Droidline 看到的内容:
droidline dump screen.json
droidline screenshot screen.pngd.dump("screen.json")
d.screenshot("screen.png")await d.dump("screen.json");
await d.screenshot("screen.png");在编辑器中打开 screen.json,与截图并排放置,搜索你在手机上看到的文字。每个匹配项都是一个元素;它周围的字段就是你可以使用的选项:
{
"text": "Keep me signed in",
"id": "dev.droidline.demo:id/auto_login",
"desc": "",
"class": "android.widget.CheckBox",
"bounds": [60, 860, 1020, 960],
"clickable": true,
"checkable": true,
"checked": false
}
bounds 是以像素为单位的 [left, top, right, bottom]。如果不确定文件中的哪个元素对应屏幕上的哪个东西,就把 bounds 和截图对照:上面这个元素横向从 60 到 1020 像素,纵向从 860 到 960 像素。
挑选选择器
选择器由一个字段和一个值组成。按以下顺序挑选:
id,如果元素有的话。 ID 由应用的开发者设定,不会随手机语言变化,大多数在应用更新后也不变。只写:id/之后的部分:("id", "auto_login")。text,用于按钮和标签。 在代码中一目了然,但它是显示出来的文字,所以会随语言变化,有时也会随更新变化。如果文字中有会变化的部分,例如数量,就使用textContains:("textContains", "unread")能匹配“3 unread”。desc,用于图标。 图标按钮通常不显示文字,但带有供屏幕阅读器使用的描述,例如“Search”或“More options”。class加nth,作为最后的手段。 “第三个复选框”这种写法,布局一变就会失效。
d.touch("id", "auto_login")
d.touch("text", "Log in")
d.touch("textContains", "unread")
d.touch("desc", "More options")
d.touch("class", "android.widget.CheckBox", nth=2)await d.touch("id", "auto_login");
await d.touch("text", "Log in");
await d.touch("textContains", "unread");
await d.touch("desc", "More options");
await d.touch("class", "android.widget.CheckBox", { nth: 2 });droidline touch id auto_login
droidline touch text "Log in"
droidline touch textContains unread
droidline touch desc "More options"
droidline touch class android.widget.CheckBox --nth 2无法直接点击的文字
文字常常是一个更大的可点击行中的标签,而标签本身不可点击。你不需要去找那一行:touch 会发现标签不接受点击,转而点击离它最近的可点击父元素。发生这种情况时,回复中会有 "via": "parent"。
如果标签位于你要操作的控件旁边,例如设置行末尾的开关,点击标签所在的行通常就能切换开关。如果不行,就直接选中开关本身,通常用 class 和 nth。
多个元素匹配
有多个元素匹配时,命令作用于按屏幕顺序的第一个。用 nth 选择其他元素(从 0 开始计数),用 count 查看一共有几个:
print(d.count("text", "Delete")) # 3
d.touch("text", "Delete", nth=1) # 第二个console.log(await d.count("text", "Delete")); // 3
await d.touch("text", "Delete", { nth: 1 }); // 第二个droidline count text Delete
droidline touch text Delete --nth 1列表中更靠下的元素
dump 只包含屏幕上显示的内容,某些列表会多包含一点。更靠下的元素此时还不存在,所以 touch 找不到它。scroll_to 会一直滑动,直到它出现,然后你再操作它:
d.scroll_to("text", "Developer options")
d.touch("text", "Developer options")
d.scroll_to("text", "Airplane mode", direction="up") # 向上查找await d.scrollTo("text", "Developer options");
await d.touch("text", "Developer options");
await d.scrollTo("text", "Airplane mode", { direction: "up" }); // 向上查找droidline scroll_to text "Developer options"
droidline touch text "Developer options"滑动 20 次(max_swipes)后仍未找到,它就会放弃,并返回 NOT_FOUND。
弹窗、键盘和状态栏
默认情况下,dump 显示的是前台应用。弹出的对话框属于这个应用,会正常显示。要同时看到键盘、状态栏或由 Android 自己绘制的权限对话框等系统窗口,请要求获取所有窗口:
d.dump("all.json", all_windows=True)await d.dump("all.json", { all_windows: true });droidline dump all.json --all_windows对于可能出现也可能不出现的弹窗,用条件检查,不要等待它们:
if d.exists("text", "Allow"):
d.touch("text", "Allow")if (await d.exists("text", "Allow")) await d.touch("text", "Allow");[ "$(droidline exists text Allow)" = "true" ] && droidline touch text Allow网页和 WebView
Chrome 中的页面和应用内的 WebView 通常也会把链接、按钮和表单字段作为元素提供出来,可见文字在 text 中,有时表单字段的标签在 desc 中。像对待其他界面一样对待它们:先 dump。如果网页在 dump 中没有任何有用的内容,说明它是在 canvas 上绘制的;请按下一节的说明使用坐标。
没有元素的界面
游戏、视频播放器、地图和一些自行绘制界面的应用完全不提供元素:dump 中只有一个大方框。在这些界面上,使用坐标和颜色:
d.tap(540, 1650) # 点击一个点,单位为屏幕像素
print(d.color(540, 1650)) # "#FF6B21":该点的颜色
d.swipe(540, 1600, 540, 400, 300) # 在 300 毫秒内从一个点拖到另一个点await d.tap(540, 1650);
console.log(await d.color(540, 1650));
await d.swipe(540, 1600, 540, 400, 300);droidline tap 540 1650
droidline color 540 1650
droidline swipe 540 1600 540 400 300从原始尺寸的截图中获取坐标:图片中的像素位置就是手机的屏幕像素。color 让你在点击之前确认按钮已经出现,例如等到某个像素变成预期的颜色。
选择器匹配不到时
touch 在超时后以 NOT_FOUND 失败,并告诉你当时屏幕上显示的是什么:
NOT_FOUND: Could not find text 'Log in' within 10s. Current screen: com.example / .MainActivity
按顺序检查以下几点:
- 是你预期的界面吗? 消息中写明了当时显示的应用和界面。弹窗、广告或很慢的网络可能把你带到了别处。
- 文字完全正确吗? 匹配要求完全一致,包括大小写、空格和标点。从 dump 中复制值,不要手动输入。如果其中一部分会变化,使用
textContains。 - 它已经在屏幕上了吗? 如果在屏幕下方还没显示出来,使用
scroll_to。如果还在加载,传入更长的timeout。 - 它在另一个窗口中吗? 使用
all_windows=True进行 dump。 - 这个界面有元素吗? 如果 dump 中只有一个大方框,就使用坐标。