牙叔教程 简单易懂
之前的两个教程:
一个写了绘制绿框,
一个写了查看控件信息,
今天我们来写 在布局层次中查看
UI构成
- 全屏悬浮窗
- recyclerView
- 选中的view的classname显示为红色, 相关的父子级也显示为红色
- 选中的view在屏幕中用红色矩形绘制出来
数据特征
- 控件信息显示有顺序性
- 控件是有状态的, 展开或者折叠
- 控件是有层级的, 一层一层的
要展示那些数据
只展示必要的数据, 那些数据是必要的?
根节点是必要的, 以及与选中的节点相关的父子层级的节点
解析节点数据
之前的教程, 绘制绿框, 我们使用的是广度搜索, 这个教程呢?
因为这个列表是从上往下, 前面显示完所有的子孙后代的控件,
然后再显示之后的层级控件,
因此, 这里我们使用 深度搜索 Depth First Search
虽然说我们只在UI显示必要的数据, 但是在UI之后的数据, 必定要是完整的数据, 不然你的数据去哪里拿呢?
遍历节点
依然是从根节点开始
let windowRoot = auto.rootInActiveWindow;
log(windowRoot);
深度搜索 Depth First Search
这是DFS方式的递归, 通常递归都是dfs
function findViewNodesByDFS(viewNode) {
let viewNodes = [];
// 节点入库
viewNodes.push(viewNode);
// 遍历节点
let childCount = viewNode.childCount();
for (let i = 0; i < childCount; i++) {
let childViewNode = viewNode.child(i);
let childViewNodes = findViewNodesByDFS(childViewNode);
viewNodes = viewNodes.concat(childViewNodes);
}
return viewNodes;
}
这个数据够吗?
不够, 只有节点信息, 没有节点状态, 也没有节点的层级信息;
节点的状态是展开还是折叠, 这个我们要知道;
节点在第几层, 我们也要知道;
所以, 在遍历节点的时候, 我们要把状态和层级也加上去
function findViewNodesByDFS(viewNode, parentViewNodeObj, childViewNodeIndex) {
let viewNodeObjs = [];
let viewNodeObj = null;
if (!parentViewNodeObj) {
// 处理根节点
viewNodeObj = {
viewNode: viewNode,
_level: 0,
_levelStr: "0",
_expand: false,
};
viewNodeObjs.push(viewNodeObj);
} else {
// 其他节点
viewNodeObj = {
viewNode: viewNode,
_level: parentViewNodeObj._level + 1,
_levelStr: parentViewNodeObj._levelStr + "-" + childViewNodeIndex,
_expand: false,
};
viewNodeObjs.push(viewNodeObj);
}
let childCount = viewNode.childCount();
for (let i = 0; i < childCount; i++) {
let childViewNode = viewNode.child(i);
let childViewNodes = findViewNodesByDFS(childViewNode, viewNodeObj, i);
viewNodeObjs = viewNodeObjs.concat(childViewNodes);
}
return viewNodeObjs;
}
在上面这个代码中, 我们增加了三个属性
- level 节点层级
- levelStr 节点层级的字符串表示
- expand 是否展开状态
这个遍历之后, 我们就获得了这个这个教程必备的数据, 相当于盖房子打了地基
打印看看
[ { viewNode: UiObject(id=null, sourceNodeId=-2147483650, packageName=com.sorcererxw.intentinterceptor, className=android.widget.FrameLayout, text=null, desc=null, indexInParent=-1, boundsInParent=[0,0][1080,1920], boundsInScreen=[0,0][1080,1920], checkable=false, checked=false, focusable=false, focused=false, selected=false, clickable=false, longClickable=false, enabled=true, password=false, scrollable=false),
_level: 0,
_levelStr: '0',
_expand: false },
{ viewNode: UiObject(id=null, sourceNodeId=-4294967295, packageName=com.sorcererxw.intentinterceptor, className=android.widget.LinearLayout, text=null, desc=null, indexInParent=0, boundsInParent=[0,0][1080,1920], boundsInScreen=[0,0][1080,1920], checkable=false, checked=false, focusable=false, focused=false, selected=false, clickable=false, longClickable=false, enabled=true, password=false, scrollable=false),
_level: 1,
_levelStr: '0-0',
_expand: false },
...
]
显示节点列表界面
let window = floaty.rawWindow(
<frame padding="8" bg="#ccffffff">
<androidx.recyclerview.widget.RecyclerView id="recyclerView"></androidx.recyclerview.widget.RecyclerView>
</frame>
);
目前就只放一个recyclerview, 后期需要修改, 再改;
条目xml
<vertical>
<horizontal>
<img h="*">
</img>
<text textColor="#000000" gravity="center_vertical" h="*">
</text>
</horizontal>
<View w="*" h="2px" bg="#33000000">
</View>
</vertical>
一个img放文字左侧的图片,
一个text放className
一个view作下划线
加载节点数据
我们先加载所有节点看看效果
let rv = window.recyclerView;
let layoutManager = new LinearLayoutManager(context);
// 设置布局管理器
rv.setLayoutManager(layoutManager);
// 设置为垂直布局,这也是默认的
layoutManager.setOrientation(OrientationHelper.VERTICAL);
let recycleAdapter = createGiftBoxAdapter(viewNodesByDFS);
rv.setAdapter(recycleAdapter);
recycleAdapter.notifyDataSetChanged();
这个demo界面写出来了, 今天先到这, 有时间再写剩下的内容
环境
设备: 小米11pro
Android版本: 12
雷电模拟器:9.0.17
Android版本: 9
Autojs版本: 9.2.13
名人名言
思路是最重要的, 其他的百度, bing, stackoverflow, github, 安卓文档, autojs文档, 最后才是群里问问 --- 牙叔教程
声明
部分内容来自网络 本教程仅用于学习, 禁止用于其他用途