是否可以使用 adb 命令通过查找 ID 单击视图?
Is it possible to use adb commands to click on a view by finding its ID?
假设我有一个应用程序(在 debug/release build 中,是否由我制作),它有一个特定视图的 ID。
是否可以调用adb命令来点击这个视图?
我知道可以让它点击特定坐标,但是否可以使用 ID 代替?
我问这个是因为我知道 "Layout Inspector" 工具(可通过 Android Studio 获得)和 "View hierarchy" 工具(可通过 "Android Device Monitor" 获得,之前通过DDMS) 可以显示视图的 ID(甚至它们的坐标和边界框),因此在执行某些自动测试时,它可能是一种更好的模拟触摸的方法。
如果需要,我可以使用 root 方法。
编辑:我已经设置了赏金,以防有一种 easier/better 方式比我在自己的答案中所写的方式要解析 "adb shell dumpsys activity top" 的结果。
我想知道是否可以获取屏幕上显示的视图坐标(当然还有大小),包括尽可能多的关于它们的信息(以识别每个坐标)。
这也应该可以通过设备实现。也许与 "monitor" 工具具有相同输出数据的东西:
注意它如何获取视图的基本信息,包括文本、id 和每个视图的边界。
据我所知,这可能可以通过 AccessibilityService 实现,但遗憾的是我不明白它是如何工作的,它的功能是什么,如何触发它,它的要求是什么等等...
根据上面评论中 @pskink 的解释,我是这样实现的:
首先,我运行这个命令:
adb shell dumpsys activity top
然后,我用这段代码来解析它:
public class ViewCoordsGetter {
public static Rect getViewBoundyingBox(String viewIdStr) {
final List<String> viewHierarchyLog = //result of the command
for (int i = 0; i < viewHierarchyLog.size(); ++i) {
String line = viewHierarchyLog.get(i);
if (line.contains(":id/" + viewIdStr + "}")) {
Rect result = getBoundingBoxFromLine(line);
if (i == 0)
return result;
int currentLineStart = getStartOfViewDetailsInLine(line);
for (int j = i - 1; j >= 0; --j) {
line = viewHierarchyLog.get(j);
if ("View Hierarchy:".equals(line.trim()))
break;
int newLineStart = getStartOfViewDetailsInLine(line);
if (newLineStart < currentLineStart) {
final Rect boundingBoxFromLine = getBoundingBoxFromLine(line);
result.left += boundingBoxFromLine.left;
result.right += boundingBoxFromLine.left;
result.top += boundingBoxFromLine.top;
result.bottom += boundingBoxFromLine.top;
currentLineStart = newLineStart;
}
}
return result;
}
}
return null;
}
private static int getStartOfViewDetailsInLine(String s) {
int i = 0;
while (true)
if (s.charAt(i++) != ' ')
return --i;
}
private static Rect getBoundingBoxFromLine(String line) {
int endIndex = line.indexOf(',', 0);
int startIndex = endIndex - 1;
while (!Character.isSpaceChar(line.charAt(startIndex - 1)))
--startIndex;
int left = Integer.parseInt(line.substring(startIndex, endIndex));
startIndex = endIndex + 1;
endIndex = line.indexOf('-', startIndex);
endIndex = line.charAt(endIndex - 1) == ',' ? line.indexOf('-', endIndex + 1) : endIndex;
int top = Integer.parseInt(line.substring(startIndex, endIndex));
startIndex = endIndex + 1;
endIndex = line.indexOf(',', startIndex);
int right = Integer.parseInt(line.substring(startIndex, endIndex));
startIndex = endIndex + 1;
//noinspection StatementWithEmptyBody
for (endIndex = startIndex + 1; Character.isDigit(line.charAt(endIndex)); ++endIndex)
;
int bot = Integer.parseInt(line.substring(startIndex, endIndex));
return new Rect(left, top, right, bot);
}
}
假设我有一个应用程序(在 debug/release build 中,是否由我制作),它有一个特定视图的 ID。
是否可以调用adb命令来点击这个视图?
我知道可以让它点击特定坐标,但是否可以使用 ID 代替?
我问这个是因为我知道 "Layout Inspector" 工具(可通过 Android Studio 获得)和 "View hierarchy" 工具(可通过 "Android Device Monitor" 获得,之前通过DDMS) 可以显示视图的 ID(甚至它们的坐标和边界框),因此在执行某些自动测试时,它可能是一种更好的模拟触摸的方法。
如果需要,我可以使用 root 方法。
编辑:我已经设置了赏金,以防有一种 easier/better 方式比我在自己的答案中所写的方式要解析 "adb shell dumpsys activity top" 的结果。
我想知道是否可以获取屏幕上显示的视图坐标(当然还有大小),包括尽可能多的关于它们的信息(以识别每个坐标)。 这也应该可以通过设备实现。也许与 "monitor" 工具具有相同输出数据的东西:
注意它如何获取视图的基本信息,包括文本、id 和每个视图的边界。
据我所知,这可能可以通过 AccessibilityService 实现,但遗憾的是我不明白它是如何工作的,它的功能是什么,如何触发它,它的要求是什么等等...
根据上面评论中 @pskink 的解释,我是这样实现的:
首先,我运行这个命令:
adb shell dumpsys activity top
然后,我用这段代码来解析它:
public class ViewCoordsGetter {
public static Rect getViewBoundyingBox(String viewIdStr) {
final List<String> viewHierarchyLog = //result of the command
for (int i = 0; i < viewHierarchyLog.size(); ++i) {
String line = viewHierarchyLog.get(i);
if (line.contains(":id/" + viewIdStr + "}")) {
Rect result = getBoundingBoxFromLine(line);
if (i == 0)
return result;
int currentLineStart = getStartOfViewDetailsInLine(line);
for (int j = i - 1; j >= 0; --j) {
line = viewHierarchyLog.get(j);
if ("View Hierarchy:".equals(line.trim()))
break;
int newLineStart = getStartOfViewDetailsInLine(line);
if (newLineStart < currentLineStart) {
final Rect boundingBoxFromLine = getBoundingBoxFromLine(line);
result.left += boundingBoxFromLine.left;
result.right += boundingBoxFromLine.left;
result.top += boundingBoxFromLine.top;
result.bottom += boundingBoxFromLine.top;
currentLineStart = newLineStart;
}
}
return result;
}
}
return null;
}
private static int getStartOfViewDetailsInLine(String s) {
int i = 0;
while (true)
if (s.charAt(i++) != ' ')
return --i;
}
private static Rect getBoundingBoxFromLine(String line) {
int endIndex = line.indexOf(',', 0);
int startIndex = endIndex - 1;
while (!Character.isSpaceChar(line.charAt(startIndex - 1)))
--startIndex;
int left = Integer.parseInt(line.substring(startIndex, endIndex));
startIndex = endIndex + 1;
endIndex = line.indexOf('-', startIndex);
endIndex = line.charAt(endIndex - 1) == ',' ? line.indexOf('-', endIndex + 1) : endIndex;
int top = Integer.parseInt(line.substring(startIndex, endIndex));
startIndex = endIndex + 1;
endIndex = line.indexOf(',', startIndex);
int right = Integer.parseInt(line.substring(startIndex, endIndex));
startIndex = endIndex + 1;
//noinspection StatementWithEmptyBody
for (endIndex = startIndex + 1; Character.isDigit(line.charAt(endIndex)); ++endIndex)
;
int bot = Integer.parseInt(line.substring(startIndex, endIndex));
return new Rect(left, top, right, bot);
}
}