# RelativeLayout

> 通过锚点进行定位和调整尺寸——相对于容器或相对于另一个组件。

Source: https://crossfly.dev/zh/docs/ui/layouts/relative-layout — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# RelativeLayout

通过**锚点**为每个子组件定位——相对于容器**或相对于另一个组件**。它独有的强项是**将 A 锚定到 B**（头像角上的徽标、图标右侧的标签）。

> 🔴 · **包：** `dev.crossfly.ui.layout` · **继承自：** `Layout`

## 何时使用
- 将一个组件锚定到**另一个**组件（`AFTER`、`BEFORE`、`END_OF`、`CENTER_OF`）。
- 精细的叠加与组合（徽标、印章、定位的标签）。

## 何时不要使用
- "顶部/内容/底部填满剩余空间"的骨架 → **优先**使用配合 `LAY_FILL` 的
  [VerticalLayout](/docs/ui/layouts/vertical-layout)（更简单；参见下方关于 `FILL`/`FIT` 的说明）。

## `add(...)` — 签名与语义
```java
public RelativeLayout add(Component alvo, RelPos x, RelPos y, RelSize larg, RelSize alt);
// + 重载：  (..., Component ref)   (..., Gap outgap)   (..., Component ref, Gap outgap)
```
没有 `ref` 时，锚点相对于**上一个**添加的组件。

**`RelPos`**（位置，位于 `UIConstants`）：`START`、`CENTER`、`END`、`AFTER`、`BEFORE`、`CENTER_OF`、
`END_OF`、`SAMEPOS`、`AUTOPOS`、`AUTOPOSCENTERED`、`KEEPPOS`。

**`RelSize`**（尺寸）：`AUTOSIZE`、`FILL`、`FIT`、`SAMESIZE`、`KEEPSIZE`。

## 完整示例（复制、粘贴并运行）
```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.graphics.*;

/** 可执行的主界面。将 launcher 指向此类（classname）并运行：gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();

      // 头像 + 头像角上的徽标 + 旁边的名字
      RelativeLayout rl = new RelativeLayout();
      Label avatar = new Label("👤");
      avatar.getStyle().autoWidth(Relative.ofFont(3f)).autoHeight(Relative.ofFont(3f));
      Label badge = new Label("9");
      Label nome  = new Label("Ana Silva");

      rl.add(avatar, RelPos.START,  RelPos.START,     RelSize.AUTOSIZE, RelSize.AUTOSIZE);
      rl.add(badge,  RelPos.END_OF, RelPos.START,     RelSize.AUTOSIZE, RelSize.AUTOSIZE, avatar); // 头像的角
      rl.add(nome,   RelPos.AFTER,  RelPos.CENTER_OF, RelSize.AUTOSIZE, RelSize.AUTOSIZE, avatar); // 头像之后

      Component cabecalho = new Component();
      cabecalho.setLayout(rl);

      setLayout(new VerticalLayout(Position.START, Position.START));
      add(LAY_AUTO, cabecalho);
      add(LAY_FILL, new Component());
   }
}
```

> 下面的片段只展示**核心部分**（放进 `onShow()` 里的内容）。要运行它们，请将其放入上面的 `MainWindow` 骨架中。

## 基本用法
```java
// 头像 + 头像角上的徽标 + 旁边的名字
RelativeLayout rl = new RelativeLayout();
ImageView avatar = new ImageView(/* 照片 */);
avatar.getStyle().autoWidth(Relative.ofFont(3f)).autoHeight(Relative.ofFont(3f));
Label badge = new Label("9");
Label nome  = new Label("Ana Silva");

rl.add(avatar, RelPos.START,  RelPos.START,     RelSize.AUTOSIZE, RelSize.AUTOSIZE);
rl.add(badge,  RelPos.END_OF, RelPos.START,     RelSize.AUTOSIZE, RelSize.AUTOSIZE, avatar); // 头像的角
rl.add(nome,   RelPos.AFTER,  RelPos.CENTER_OF, RelSize.AUTOSIZE, RelSize.AUTOSIZE, avatar); // 头像之后

Component cabecalho = new Component();
cabecalho.setLayout(rl);
```

## 有用的锚定模式
- **B 在 A 右侧并对齐：** `add(b, RelPos.AFTER, RelPos.CENTER_OF, RelSize.AUTOSIZE, RelSize.AUTOSIZE, a)`。
- **与 A 相同尺寸：** `RelSize.SAMESIZE`。
- **居中卡片：** `add(card, RelPos.CENTER, RelPos.CENTER, RelSize.AUTOSIZE, RelSize.AUTOSIZE)`。
- **按规则设定边距：** 将 `Gap.ofFont(0.5f)` 作为最后一个参数传入（它会成为该规则的 `outgap`）。

## ⚠️ `RelSize` 的确切语义（不要混淆）
- **`FILL`** = 父容器的**全部**宽度/高度减去边距——**忽略参照物**（不是"A 旁边剩下的空间"）。
- **`FIT`** = 从**父容器的角**到**参照物的边**（例如：填充**到** A 的左侧/顶部）。
- 要实现"某个子组件吸收其他组件旁边的剩余空间" → 使用配合 `LAY_FILL` 的
  `HorizontalLayout`/`VerticalLayout`，而**不是** `RelativeLayout`。

## 常见错误
- 期望 `FILL` 是"A 旁边剩下的空间"（它其实是整个父容器）。
- 引用的**循环**（A 依赖 B，而 B 又依赖 A）——务必锚定到之前已经添加的组件。

## 另请参阅
- [VerticalLayout](/docs/ui/layouts/vertical-layout)（用于屏幕骨架） · [响应式设计](/docs/ui/layouts/responsiveness)
