diff --git a/tools/deobf/anchors.tsv b/tools/deobf/anchors.tsv index 8d3e1a5..c3e4ec3 100644 --- a/tools/deobf/anchors.tsv +++ b/tools/deobf/anchors.tsv @@ -93,3 +93,8 @@ dR V (F)LdR; prefHeight # 现象佐证:ScrollPane 默认 childrenOnly 时自己不是命中目标,收不到 enter → 滚轮事件不会送到它, # 但程序化 scrollY() 是生效的(实测 before=0 after=200)。 dl L (Ldv;)V setTouchable +# ---- Cell.height(2026-10-16 实测确认)---- +# 依据:对 Cell 的候选 (float) 方法依次调用 900,等一帧后量"根表格高度": +# Cell.I(900) -> root 高度 580 -> 1080(其它候选 L/B/Z/V/C/S/F 都没这个效果) +# => Cell.I(float) 就是 height(float);现象佐证:设了它之后列表区出现滚动条、可滚。 +dR I (F)LdR; height diff --git a/tools/deobf/names.tsv b/tools/deobf/names.tsv index 0510822..c693fc7 100644 --- a/tools/deobf/names.tsv +++ b/tools/deobf/names.tsv @@ -1,4 +1,4 @@ -# 合并后的名字映射表:**只用确定(=)的指纹命中 + 手动/实测锚点** +# 合并后的名字映射表:确定(=)指纹 + 手动/实测锚点 # owner name desc newName GO C F width GO I F scaleY diff --git a/viewpanel/src/com/spineview/ObjectPanel.java b/viewpanel/src/com/spineview/ObjectPanel.java index 66620eb..807c96b 100644 --- a/viewpanel/src/com/spineview/ObjectPanel.java +++ b/viewpanel/src/com/spineview/ObjectPanel.java @@ -148,6 +148,13 @@ public final class ObjectPanel { layoutCell(logCell, Obf.CELL_LEFT); cell = call(root, Obf.TABLE_ADD, new Class[] {actorClass}, cellClass, content); layoutCell(cell, Obf.CELL_EXPAND, Obf.CELL_FILL); + // 中段高度固定:Cell.height(float) 在载荷里叫 `I`(描述符 (F)LdR;) + // 实测确认:设 900 后根表格高度 580 -> 1080,说明它就是 height()。 + try { + call(cell, "I", new Class[] {float.class}, cellClass, Float.valueOf(700f)); + } catch (Throwable error) { + System.err.println("[panel] 设置中段高度失败: " + error); + } wire(loader, setValue, new Runnable() { @Override @@ -191,67 +198,6 @@ public final class ObjectPanel { }); startSelectionWatcher(state); - // 一次性诊断:等布局稳定后把 ScrollPane / 日志 Label 的几何打出来(只读,不改行为) - Thread measure = new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(2500); - } catch (InterruptedException ignored) { - return; - } - StringBuilder a = new StringBuilder("[panel] 列表 ScrollPane 几何: "); - for (Method m : state.listScroll.getClass().getMethods()) { - if (m.getParameterTypes().length != 0 || m.getReturnType() != float.class) continue; - try { - m.setAccessible(true); - a.append(m.getName()).append('=').append(m.invoke(state.listScroll)).append(' '); - } catch (Throwable ignored) { - // 跳过 - } - } - StringBuilder b = new StringBuilder("[panel] 日志 Label 几何: "); - for (Method m : state.status.getClass().getMethods()) { - if (m.getParameterTypes().length != 0 || m.getReturnType() != float.class) continue; - try { - m.setAccessible(true); - b.append(m.getName()).append('=').append(m.invoke(state.status)).append(' '); - } catch (Throwable ignored) { - // 跳过 - } - } - // 同一方法名在 root/content/pane 上各读一遍:真正的 getWidth/getHeight 会给出"面板那么大"的数 - for (Method m : state.listScroll.getClass().getMethods()) { - if (m.getParameterTypes().length != 0 || m.getReturnType() != float.class) continue; - String name = m.getName(); - StringBuilder row = new StringBuilder("[panel] " + name + ": "); - Object[] targets = {state.root, state.content, state.listScroll, state.status}; - String[] tags = {"root", "content", "pane", "log"}; - for (int i = 0; i < targets.length; i++) { - if (targets[i] == null) continue; - try { - Method same = null; - for (Method candidate : targets[i].getClass().getMethods()) { - if (candidate.getName().equals(name) - && candidate.getParameterTypes().length == 0 - && candidate.getReturnType() == float.class) { - same = candidate; - break; - } - } - if (same == null) continue; - same.setAccessible(true); - row.append(tags[i]).append('=').append(same.invoke(targets[i])).append(' '); - } catch (Throwable ignored) { - // 跳过 - } - } - System.err.println(row.toString()); - } - } - }, "spineview-measure"); - measure.setDaemon(true); - measure.start(); System.err.println("[panel] 增删改查面板已建好"); refreshList(state); return root; @@ -817,6 +763,21 @@ public final class ObjectPanel { } } + /** 读某个对象的某个无参 float 方法(诊断用)。 */ + static float floatOf(Object target, String name) { + try { + for (Method m : target.getClass().getMethods()) { + if (!m.getName().equals(name)) continue; + if (m.getParameterTypes().length != 0 || m.getReturnType() != float.class) continue; + m.setAccessible(true); + return ((Float) m.invoke(target)).floatValue(); + } + } catch (Throwable ignored) { + // 读不到 + } + return -1f; + } + private static float readFloat(Object target, String name) { try { for (Method method : target.getClass().getMethods()) {