Info

绘制基类:树形子节点、尺寸测量

基本信息

属性 值
路径 com.github.lunatrius.ingameinfo.client.gui.Info
行数 90
子类 InfoText、InfoItem、InfoIcon

三个子类构成的渲染树

public final List<Info> children = new ArrayList<>();   // :14
子类 渲染对象
InfoText 一行文本(根节点,持有 Value 列表)
InfoItem 物品堆
InfoIcon 纹理图集子矩形

InfoText 是唯一会挂子节点的: ValueComplex 的 ValueIcon 生成 InfoIcon 并挂到父 InfoText 上。

draw(:31-40)—— 先自己,后子节点

public void draw() {
    drawInfo();                                    // :32  自己先画
    for (Info child : this.children) {             // :34
        child.offsetX = this.x;                    // :35  ⚠️ 覆盖而非累加
        child.offsetY = this.y;                    // :36
        child.draw();
    }
}

⚠️ 子节点的 offsetX/Y 被直接赋值为父的 x/y,不是累加 —— 所以渲染树最多只能有一层深。 若 InfoText → InfoIcon → 再挂子节点,第三层的偏移会变成第二层的 x/y 而非相对坐标,实际会错位。

⚠️ drawInfo() 在 for 循环之前(:32), 所以子节点绘制在自己之上(覆盖)。

位置与偏移

字段 行 说明
x / y :15-16 自身绝对坐标
offsetX / offsetY :17-18 父节点坐标,由 draw() 赋值
hasPosition :21 public boolean,初值 false
getX() :44-46 x + offsetX
getY() :48-50 y + offsetY

⚠️ hasPosition 是 public 可变字段(不是 private), 子类直接读写(InfoText 的 updateChildren 在 :74、:77 读写它)。 初值 false,由 updateChildren 置 true。

⚠️ Info.draw() 自身不检查 hasPosition —— 只有 InfoText 的 updateChildren 用它来跳过 已定位过的子节点。children 列表(:14)与 attachedValues (InfoText 的 :19)是两套容器,本类不区分。

尺寸:基类一律返回 0

public int getWidth()  { return 0; }   // :60-62
public int getHeight() { return 0; }   // :64-66

⚠️ 基类不测量自身尺寸,返回 0。 三个子类全部覆写了这两个方法,所以实际使用没问题。 但若新增子类忘记覆写,它的宽度为 0, 在 InfoText 的布局里会被当成「零宽」节点。

oldWidth 与 iconSpacing:图标占位空格

private String iconSpacing = "";     // :20
private int oldWidth;                // :22

public String getIconSpacing() {               // :52-58
    if (oldWidth != getWidth()) {              // 宽度变了才重算
        setIconSpacing();
        oldWidth = getWidth();
    }
    return iconSpacing;
}

private void setIconSpacing() {                // :78-84
    String str = "";
    for (int i = 0; i < getWidth() && fontRenderer.getStringWidth(str) < getWidth(); i++) {
        str += " ";
    }
    iconSpacing = str;
}

用途:为前置图标占位。 循环同时受 i < getWidth() 与 getStringWidth(str) < getWidth() 双重约束 —— 即空格数既不超过宽度值、也不超过该宽度对应的像素宽度, 所以对宽字符(CJK)会提前停止。

⚠️ oldWidth 初值为 0(Java 默认), 而 getWidth() 在子类里通常非 0,所以首次调用必然触发重算。 若某子类 getWidth() 恒返回 0(如忘记覆写),则永不重算, iconSpacing 保持 ""。

⚠️ setIconSpacing 是 private(:78), 外部无法强制重算;只能靠改变 getWidth() 的返回值来触发。

⚠️ 用空格而非制表符占位 —— 等宽字体下精确,但比例字体下空格宽度不均, 且末尾空格可能被渲染器裁掉。

文本标识与赋值

成员 行 说明
identifier :19 初值 ""(空串,非 null)
setIdentifier(@NotNull String) :68-70 覆写时需处理 @NotNull 契约
getIdentifier() :74-76 —
setValue(@NotNull Object) :72 空实现,子类覆写
toString() :86-89 固定返回字面量 "Info"

⚠️ toString() 不输出类型也不输出 identifier, 三个子类若不覆写就都显示 Info。实际 InfoText 等都覆写了 toString(各有专门条目)。

identifier 的实际用途见 TagPlayerPotion 的 shouldUpdate (:200-203)—— 用 Info.identifier 比对药水 ID 来判断图标是否需要重建。所以 identifier 是缓存失效键。

字体渲染器

protected static final FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer;   // :13

protected static final —— 类加载时取 Minecraft 单例的 fontRenderer。 三个子类都用它测文本宽度。

相关