# 如何使用复选框和单选框（Option 和 Group）

> 单独的一个 Option 就是复选框；同一个 Group 中的多个 Option 就是单选框（互斥选择）。

Source: https://crossfly.dev/zh/docs/cookbook/ui/checkbox-e-radio — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 如何使用复选框和单选框（Option 和 Group）

**目的。** 提供可勾选的选项。一个独立的 `Option` 作为**复选框**
（独立的开/关）工作。同一个 `Group` 中的多个 `Option` 作为**单选
按钮**（一次只能选中一个）工作。复选框用于独立选择；单选框
用于在多个备选项之间的唯一选择。

## 所用 API
- `new Option()` / `new Option(String text)` — 创建选项（带或不带标签）。初始为未勾选。
- `option.checked(boolean checked)` — 设置初始状态，**不带动画**（流式）。
- `option.setChecked(boolean checked)` — 以交叉淡入淡出**动画**改变状态。
- `option.isChecked()` — 返回当前状态（`boolean`）。
- `option.toggle()` — 反转状态（在点击时被内部调用）。
- `option.onAction(Component.Action action)` — 当用户点击**导致状态改变时**触发的回调。
- `new Group()` — 互斥选择的分组。
- `option.group(Group g)` — 将选项关联到分组（转变为单选框）。
- `group.fade(boolean)` — 启用切换选择时的颜色动画过渡。

> 不易察觉的行为（已在源码中确认）：
> - 在 `Group` 中，点击**已选中**的选项不会取消其选中（如果 `checked && getGroup() != null`，`toggle()` 会提前返回）—— 与单选框相同：只有在选择另一项时才切换。
> - `onAction` 只在状态确实改变时触发（`if (checked != prev) runActions()`）。
> - 在分组的某个选项上通过 `checked(true)` 进行勾选，会立即通知分组并取消其余选项的选中。

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

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

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

      // ── Checkbox（独立的 Option）──
      Option aceito = new Option("Aceito os termos");
      aceito.checked(false);                 // 初始状态，无动画
      aceito.onAction(o -> atualizarBotao(aceito.isChecked()));
      add(aceito);

      // ── Radio（同一 Group 内的多个 Option）──
      Group entrega = new Group();

      Option padrao   = new Option("Entrega padrão").group(entrega);
      Option expresso = new Option("Entrega expressa").group(entrega);

      padrao.checked(true);                  // 组的初始选中项

      expresso.onAction(o -> escolherFrete("EXPRESSO"));
      padrao.onAction(o -> escolherFrete("PADRAO"));

      add(padrao);
      add(expresso);
   }

   private void atualizarBotao(boolean aceito)
   {
      // ... 根据 checkbox 启用/禁用按钮 ...
   }

   private void escolherFrete(String tipo)
   {
      // ... 响应运费选择 ...
   }
}
```

> 下面的片段只展示**核心部分**。要运行它，请把它放进上面的 `MainWindow` 骨架中。

## 最小片段
```java
// ── Checkbox（独立的 Option）──
Option aceito = new Option("Aceito os termos");
aceito.checked(false);                 // 初始状态，无动画
aceito.onAction(o -> atualizarBotao(aceito.isChecked()));
add(aceito);

// ── Radio（同一 Group 内的多个 Option）──
Group entrega = new Group();

Option padrao  = new Option("Entrega padrão").group(entrega);
Option expresso = new Option("Entrega expressa").group(entrega);

padrao.checked(true);                  // 组的初始选中项

expresso.onAction(o -> escolherFrete("EXPRESSO"));
padrao.onAction(o -> escolherFrete("PADRAO"));

add(padrao);
add(expresso);
```

> **验证说明（2026 代码）。** 已确认：`Option` 实现了 `Component.Checkable`
> 和 `AlphaAnimation.Animate`；`Group.select(sender)` 会自动取消其余成员
> （`Option`/`Switch`）的选中。`Group.fade`（默认 `false`）控制
> 切换是否带动画。`Group` 中没有「值」：要知道选中项，请检查
> 每个 `Option` 的 `isChecked()`，或在回调中保存所选的引用。

## 深入了解
- 控件页面：[Option](/docs/ui/widgets/option) · [Group](/docs/ui/widgets/group)
- 下拉框（弹出层中的单选）：[dropdown-selecao.md](/docs/cookbook/ui/dropdown-selecao)
