新增阶段五文档:面板接真实数据(增删改查/属性读写/Inspector 真名翻译/滚动焦点/统计)+ 14 条踩坑点 + 未完成清单
This commit is contained in:
1 parent
bb817b85e5
commit
7f06c6744a
43 files changed
+2942
-79
No files matched your search
@@ -0,0 +1,177 @@
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
/**
|
||||
* 只读探针 6:把面板的**整棵控件树**连坐标打出来(含 stage 坐标),
|
||||
* 用来定位"列表里的内容被画到面板外"。
|
||||
*
|
||||
* 取值方法来自字节码(javap -p -c):
|
||||
* dl.c_()F=getX dl.d_()F=getY dl.L()F=getWidth dl.I()F=getHeight
|
||||
* dl.D()Ldo; = getParent do.D()Lhq; = getChildren hq extends fI(Array),size 字段在 fI 里
|
||||
* ⚠️ et(Table) 自己也声明了 `int L()` / `int I()`(列数/行数),所以必须**按返回类型 float 过滤**。
|
||||
*/
|
||||
public final class GeomProbe6 {
|
||||
static Instrumentation INST;
|
||||
static Object STATE;
|
||||
static Class<?> HQL, ARR, AO;
|
||||
|
||||
public static void agentmain(String args, Instrumentation inst) {
|
||||
INST = inst;
|
||||
try { run(); } catch (Throwable e) { log("失败: " + e); e.printStackTrace(System.err); }
|
||||
}
|
||||
|
||||
static void log(String m) { System.err.println("[geom6] " + m); }
|
||||
|
||||
static void run() throws Exception {
|
||||
for (Class<?> c : INST.getAllLoadedClasses()) {
|
||||
if (!c.getName().equals("com.spineview.ObjectPanel")) continue;
|
||||
try {
|
||||
Field f = c.getDeclaredField("LAST_STATE");
|
||||
f.setAccessible(true);
|
||||
Object v = f.get(null);
|
||||
if (v != null) STATE = v;
|
||||
} catch (Throwable ignored) { }
|
||||
}
|
||||
if (STATE == null) { log("LAST_STATE 拿不到"); return; }
|
||||
ClassLoader payload = null;
|
||||
for (Class<?> c : INST.getAllLoadedClasses()) {
|
||||
if (c.getClassLoader() instanceof URLClassLoader) { payload = c.getClassLoader(); break; }
|
||||
}
|
||||
HQL = Class.forName("hq", true, payload);
|
||||
ARR = Class.forName("fI", true, payload);
|
||||
AO = Class.forName("dl", true, payload);
|
||||
Class<?> og = null;
|
||||
for (Class<?> c : INST.getAllLoadedClasses()) if (c.getName().equals("OG")) og = c;
|
||||
Method onRender = null;
|
||||
for (Method m : og.getDeclaredMethods()) {
|
||||
Class<?>[] t = m.getParameterTypes();
|
||||
if (m.getName().equals("B") && t.length == 1 && t[0] == Runnable.class) { onRender = m; break; }
|
||||
}
|
||||
onRender.setAccessible(true);
|
||||
onRender.invoke(null, new Runnable() {
|
||||
@Override public void run() {
|
||||
try { work(STATE); } catch (Throwable e) { log("work 失败: " + e); e.printStackTrace(System.err); }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void work(Object state) throws Exception {
|
||||
Object root = state("root");
|
||||
log("=========== 面板控件树(缩进=层级;坐标为 stage 绝对坐标)===========");
|
||||
walk(root, 0, 0f, 0f, 6);
|
||||
log("=========== 面板根再往上(谁给它多大地方)===========");
|
||||
Object p = parent(root);
|
||||
float ax = 0f, ay = 0f;
|
||||
for (int i = 0; i < 6 && p != null; i++) {
|
||||
log(" 上级[" + i + "] " + p.getClass().getName() + " " + geom(p));
|
||||
Object np = parent(p);
|
||||
if (np == p) break;
|
||||
p = np;
|
||||
}
|
||||
log("=========== 关键对象定位 ===========");
|
||||
Object scroll = state("listScroll"), table = state("listTable");
|
||||
log(" listScroll " + geom(scroll) + " 子控件=" + size(childrenOf(scroll)));
|
||||
log(" listTable " + geom(table) + " 行数=" + size(childrenOf(table)));
|
||||
log(" listTable 的 stage 坐标 = " + stageX(table) + "," + stageY(table));
|
||||
log("=======================================================");
|
||||
}
|
||||
|
||||
static Object state(String name) throws Exception {
|
||||
Field f = STATE.getClass().getDeclaredField(name);
|
||||
f.setAccessible(true);
|
||||
return f.get(STATE);
|
||||
}
|
||||
|
||||
static void walk(Object actor, int depth, float px, float py, int maxDepth) {
|
||||
if (actor == null || depth > maxDepth) return;
|
||||
StringBuilder ind = new StringBuilder();
|
||||
for (int i = 0; i < depth; i++) ind.append(" ");
|
||||
float x = fl(actor, "c_"), y = fl(actor, "d_");
|
||||
Object kids = childrenOf(actor);
|
||||
int n = size(kids);
|
||||
log(ind + actor.getClass().getName() + " " + geom(actor)
|
||||
+ " stage=(" + r(px + x) + "," + r(py + y) + ") 子控件=" + n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
Object child = get(kids, i);
|
||||
walk(child, depth + 1, px + x, py + y, maxDepth);
|
||||
}
|
||||
}
|
||||
|
||||
static float stageX(Object actor) {
|
||||
float x = 0f;
|
||||
Object a = actor;
|
||||
for (int i = 0; i < 8 && a != null; i++) { x += fl(a, "c_"); a = parent(a); }
|
||||
return r(x);
|
||||
}
|
||||
|
||||
static float stageY(Object actor) {
|
||||
float y = 0f;
|
||||
Object a = actor;
|
||||
for (int i = 0; i < 8 && a != null; i++) { y += fl(a, "d_"); a = parent(a); }
|
||||
return r(y);
|
||||
}
|
||||
|
||||
static String geom(Object actor) {
|
||||
if (actor == null) return "null";
|
||||
return "[local x=" + r(fl(actor, "c_")) + " y=" + r(fl(actor, "d_"))
|
||||
+ " w=" + r(fl(actor, "L")) + " h=" + r(fl(actor, "I"))
|
||||
+ " top=" + r(fl(actor, "S")) + " right=" + r(fl(actor, "C")) + "]";
|
||||
}
|
||||
|
||||
static Object parent(Object actor) {
|
||||
if (actor == null) return null;
|
||||
for (Method m : actor.getClass().getMethods()) {
|
||||
if (m.getParameterTypes().length != 0) continue;
|
||||
if (!m.getName().equals("D")) continue;
|
||||
if (!m.getReturnType().getName().equals("do")) continue;
|
||||
try { m.setAccessible(true); return m.invoke(actor); } catch (Throwable ignored) { }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Object childrenOf(Object actor) {
|
||||
if (actor == null) return null;
|
||||
for (Method m : actor.getClass().getMethods()) {
|
||||
if (m.getParameterTypes().length != 0) continue;
|
||||
if (m.getReturnType() != HQL) continue;
|
||||
try { m.setAccessible(true); return m.invoke(actor); } catch (Throwable ignored) { }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static int size(Object array) {
|
||||
if (array == null) return 0;
|
||||
for (Class<?> t = ARR; t != null && t != Object.class; t = t.getSuperclass()) {
|
||||
for (Field f : t.getDeclaredFields()) {
|
||||
if (f.getType() != int.class) continue;
|
||||
f.setAccessible(true);
|
||||
try { return f.getInt(array); } catch (Throwable ignored) { }
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Object get(Object array, int index) {
|
||||
if (array == null) return null;
|
||||
for (Method m : array.getClass().getMethods()) {
|
||||
if (!m.getName().equals("D")) continue;
|
||||
Class<?>[] t = m.getParameterTypes();
|
||||
if (t.length != 1 || t[0] != int.class || m.getReturnType() != Object.class) continue;
|
||||
try { m.setAccessible(true); return m.invoke(array, Integer.valueOf(index)); } catch (Throwable ignored) { }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static float fl(Object actor, String name) {
|
||||
if (actor == null) return -1f;
|
||||
for (Method m : actor.getClass().getMethods()) {
|
||||
if (!m.getName().equals(name)) continue;
|
||||
if (m.getParameterTypes().length != 0 || m.getReturnType() != float.class) continue;
|
||||
try { m.setAccessible(true); return ((Float) m.invoke(actor)).floatValue(); } catch (Throwable ignored) { }
|
||||
}
|
||||
return -1f;
|
||||
}
|
||||
|
||||
static float r(float v) { return Math.round(v * 100f) / 100f; }
|
||||
}
|
||||
Reference in new issue
Block a user