# 如何创建标签页（TabBar）

> 构建一个标签栏，为每个标签提供内容并响应标签切换。

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

# 如何创建标签页（TabBar）

**目的。** 通过标签栏将内容组织成可切换的页面，带有动画指示器和页面间的水平滑动。
用于顶级分区（首页 / 搜索 / 个人资料），或在无需屏幕导航的情况下对相关内容进行分组。

## 所用 API
- `new TabBar(TabBar.Tab... tabs)` — 从标签描述符创建标签栏。
- `new TabBar.Tab(String text)` — 仅文本的标签。
- `new TabBar.Tab(Icon icon)` — 仅图标的标签。
- `new TabBar.Tab(String text, Icon icon)` — 带文本和图标的标签（默认文本位于图标下方）。
- `tabBar.setContent(Component... contents)` — 定义每个标签的内容；**数量必须与标签数量一致**（否则抛出 `IllegalArgumentException`）。
- `tabBar.onTabChanged(TabBar.TabChangedListener listener)` — 当激活标签改变时（通过点击或滑动）调用的回调 `onTabChanged(int index)`。
- `tabBar.setActiveTab(int index)` — 以编程方式切换标签（动画指示器并滚动内容）。
- `tabBar.getActiveIndex()` — 激活标签的索引。

## 完整示例（复制、粘贴即可运行）
```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());

      // 纯文本标签页（自足，不依赖 MaterialIcon 字形）
      TabBar tabBar = new TabBar(
         new TabBar.Tab("Início"),
         new TabBar.Tab("Buscar"),
         new TabBar.Tab("Perfil"));

      // 每个标签页一个内容 Component，顺序与标签页相同
      Component telaInicio = new Label("Conteúdo: Início");
      Component telaBusca  = new Label("Conteúdo: Buscar");
      Component telaPerfil = new Label("Conteúdo: Perfil");

      tabBar.setContent(
         telaInicio,
         telaBusca,
         telaPerfil);

      tabBar.onTabChanged(index ->
      {
         // 例如：切换标签页时按需加载数据
         if (index == 1) carregarBusca();
      });

      add(LAY_FILL, tabBar);
   }

   private void carregarBusca()
   {
      // ... 按需加载"搜索"标签页的数据 ...
   }
}
```

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

## 最小片段
```java
TabBar tabBar = new TabBar(
   new TabBar.Tab("Início",  new MaterialIcon("")),
   new TabBar.Tab("Buscar",  new MaterialIcon("")),
   new TabBar.Tab("Perfil",  new MaterialIcon("")));

// 每个标签页一个内容 Component，顺序与标签页相同
tabBar.setContent(
   telaInicio,
   telaBusca,
   telaPerfil);

tabBar.onTabChanged(index ->
{
   // 例如：切换标签页时按需加载数据
   if (index == 1) carregarBusca();
});

add(tabBar);

// 如需用代码切换标签页：
tabBar.setActiveTab(2);   // 跳到"个人资料"
```

> **验证说明（2026 代码）。** 已在源码中确认：`TabBar(Tab...)`、
> `setContent(Component...)`、`onTabChanged(TabChangedListener)`、`setActiveTab(int)`。
> 行为细节：当标签放不下时，标签栏会水平滑动；
> 滑动内容面板会切换激活标签并移动指示器；
> 标签的位置（顶部还是底部）以及指示器的样式来自当前 `Skin` 的
> `TabBarStyle`（例如：`IOSSkin` 把标签放在底部）。如果未
> 调用 `setContent`，标签栏只显示标签而没有内容面板。

## 深入了解
- 控件页面：[TabBar](/docs/ui/widgets/tabbar) · 权威来源：第 14 章
- 屏幕间导航（标签的替代方案）：[导航](/docs/ui/navigation)
