阶段四收尾:面板两行布局(row/expand/fill)+ 英文输出 + 混淆名对照表
- ViewInstaller:用 Table.row()/Cell.expand()+fill()/setFillParent 排出" 按钮一行、可滚动文字区一行"的面板;修掉把 setDebug 当成 setFillParent 的误用 - Scene2dPanel:按钮 isChecked/setChecked 与 Label.setText 改用对照表定名, 修掉"点一次反复触发"与"文字不刷新"两个 bug - SceneReader:面板输出改英文(编辑器缺中文字形,中文会渲染成方块) - 新增 tools/obfmap/MapMethods.java:载荷 vs 上游 libGDX 的字节码指纹比对工具 - 新增 混淆名对照表.md:类/方法/字段映射、推导办法、已踩的坑、验证状态
This commit is contained in:
1 parent
2e7ab7c33c
commit
8f7b72271f
8 files changed
+677
-52
No files matched your search
@@ -13,10 +13,10 @@ import java.lang.reflect.Modifier;
|
||||
*/
|
||||
public final class ViewInstaller {
|
||||
private static final String SKIN = "com.badlogic.gdx.scenes.scene2d.ui.Skin";
|
||||
private static final String TABLE = "et";
|
||||
private static final String ACTOR = "dl";
|
||||
private static final String LABEL = "com.badlogic.gdx.scenes.scene2d.ui.Label";
|
||||
private static final String TEXT_BUTTON = "com.badlogic.gdx.scenes.scene2d.ui.TextButton";
|
||||
private static final String SCROLL_PANE = "com.badlogic.gdx.scenes.scene2d.ui.ScrollPane";
|
||||
|
||||
private ViewInstaller() {
|
||||
}
|
||||
@@ -189,10 +189,18 @@ public final class ViewInstaller {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 面板内容:一个 Table,里面一行按钮、一行文字显示。 */
|
||||
/**
|
||||
* 面板内容:第一行按钮(靠左),第二行是能滚动的文字区(撑满剩下的空间)。
|
||||
*
|
||||
* 布局调用全部按"名字 + 参数类型 + 返回类型"定位 —— 这套载荷把同名方法塞了一堆
|
||||
* (Table 上 `I` 既有无参返回 Cell 的 row(),也有无参返回 Table 的 top()),
|
||||
* 少判一个条件就会调到别的方法上。名字出处见 {@link Obf}。
|
||||
*/
|
||||
private static Object buildPanel(ClassLoader loader, Object skin) throws Exception {
|
||||
Class<?> skinClass = Class.forName(SKIN, true, loader);
|
||||
Class<?> tableClass = Class.forName(TABLE, true, loader);
|
||||
Class<?> tableClass = Class.forName(Obf.TABLE, true, loader);
|
||||
Class<?> cellClass = Class.forName(Obf.CELL, true, loader);
|
||||
Class<?> actorClass = Class.forName(Obf.ACTOR, true, loader);
|
||||
Class<?> buttonClass = Class.forName(TEXT_BUTTON, true, loader);
|
||||
Class<?> labelClass = Class.forName(LABEL, true, loader);
|
||||
|
||||
@@ -202,33 +210,64 @@ public final class ViewInstaller {
|
||||
Object label = labelClass.getConstructor(CharSequence.class, skinClass)
|
||||
.newInstance("Click the button to list scene objects.", skin);
|
||||
|
||||
// 注意:这里必须用 Table 的单元格布局(add/row),不要换成 addActor + 绝对定位 ——
|
||||
// 那个版本在停靠框架里会抛 "Tab not found: Spine View",把安装打断。
|
||||
Method add = null;
|
||||
Method row = null;
|
||||
for (Method method : tableClass.getMethods()) {
|
||||
if (Modifier.isStatic(method.getModifiers())) continue;
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
if (types.length == 1 && types[0].isAssignableFrom(buttonClass)
|
||||
&& method.getReturnType() != void.class) {
|
||||
if (add == null) add = method;
|
||||
}
|
||||
if (types.length == 0 && method.getReturnType() != Object.class
|
||||
&& method.getReturnType().isAssignableFrom(tableClass)) {
|
||||
if (row == null) row = method;
|
||||
}
|
||||
}
|
||||
if (add != null) {
|
||||
add.invoke(table, button);
|
||||
if (row != null) row.invoke(table);
|
||||
add.invoke(table, label);
|
||||
} else {
|
||||
System.err.println("[view] 找不到 Table.add");
|
||||
// 读出来的东西会很长,包进 ScrollPane 才能滚动查看。
|
||||
Object scrollBody = label;
|
||||
try {
|
||||
Class<?> scrollClass = Class.forName(SCROLL_PANE, true, loader);
|
||||
scrollBody = scrollClass.getConstructor(actorClass, skinClass).newInstance(label, skin);
|
||||
System.err.println("[view] 文字区已包进 ScrollPane");
|
||||
} catch (Throwable error) {
|
||||
System.err.println("[view] ScrollPane 建不出来,直接用 Label: " + error);
|
||||
}
|
||||
|
||||
// 让面板铺满停靠框(WidgetGroup.setFillParent)。
|
||||
// 注意别调成 setDebug(boolean) —— 名字都是单字母,调错只会给单元格描上彩色边框。
|
||||
call(table, Obf.TABLE_SET_FILL_PARENT, new Class<?>[] {boolean.class}, void.class,
|
||||
Boolean.TRUE);
|
||||
// 第一行:按钮,靠左。
|
||||
Object buttonCell = call(table, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, button);
|
||||
layoutCell(buttonCell, Obf.CELL_LEFT);
|
||||
// 换行:不调 row() 的话按钮和文字会并排挤在同一行。
|
||||
call(table, Obf.TABLE_ROW, new Class<?>[0], cellClass);
|
||||
// 第二行:文字区,占满剩余空间并撑开单元格。
|
||||
Object textCell = call(table, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, scrollBody);
|
||||
layoutCell(textCell, Obf.CELL_EXPAND, Obf.CELL_FILL, Obf.CELL_TOP);
|
||||
System.err.println("[view] 面板已按两行布局:按钮 / 可滚动文字区");
|
||||
|
||||
Scene2dPanel.wireButton(button, label, loader);
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 Cell 上调一串"无参、返回 Cell"的布局方法(fill/expand/top…,名字见 {@link Obf})。
|
||||
* 单个调用失败只记一行日志,不让整个面板装不上。
|
||||
*/
|
||||
private static void layoutCell(Object cell, String... names) {
|
||||
for (String name : names) {
|
||||
try {
|
||||
Method method = cell.getClass().getMethod(name);
|
||||
method.setAccessible(true);
|
||||
method.invoke(cell);
|
||||
} catch (Throwable error) {
|
||||
System.err.println("[view] 单元格布局 " + name + " 调用失败: " + error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 调用 Table 上的实例方法:名字 + 参数类型 + 返回类型一起判(同名方法太多)。 */
|
||||
private static Object call(Object target, String name, Class<?>[] parameterTypes,
|
||||
Class<?> returnType, Object... arguments) throws Exception {
|
||||
for (Method method : target.getClass().getMethods()) {
|
||||
if (!method.getName().equals(name)) continue;
|
||||
if (!java.util.Arrays.equals(method.getParameterTypes(), parameterTypes)) continue;
|
||||
if (returnType != null && method.getReturnType() != returnType) continue;
|
||||
method.setAccessible(true);
|
||||
return method.invoke(target, arguments);
|
||||
}
|
||||
throw new NoSuchMethodException(target.getClass().getName() + "." + name
|
||||
+ " 参数=" + java.util.Arrays.toString(parameterTypes));
|
||||
}
|
||||
|
||||
private static Method findInstanceMethod(Class<?> owner, int parameterCount,
|
||||
Class<?> parameterType) throws NoSuchMethodException {
|
||||
for (Method method : owner.getMethods()) {
|
||||
|
||||
Reference in new issue
Block a user