本页面由葡萄牙语机器翻译,可能存在错误。葡萄牙语版本为准。
TabBar
TabBar 是一个标签栏:一条按钮(tab strip)+ 一个可水平滚动并按页对齐的内容面板(通过 PageScrollEngine)。触摸某个标签会将内容滚动到对应页面;拖动内容会切换活动标签。一个动画指示器跟随活动标签。
美学效果(指示器、标签在顶部或底部、颜色)来自活动的 Skin(TabBarStyle)。每个标签由一个 TabBar.Tab(文本、图标或两者)描述。
🟡 · 包:
dev.crossfly.ui· 继承:Component· 样式:TabBarStyle(通过getSkin().tabBar())
何时使用
- 在一个屏幕的 2–5 个区块之间导航(首页 / 搜索 / 个人资料)。
- 分页内容,配合页面之间的滑动手势 + 同步的标签。
何时不使用
- 放不下的过多区块(strip 会滚动,但可读性下降)→ 考虑其他导航方式。
外观
等宽的标签条(1/n);如果它们放不下,strip 会变为可水平滚动,标签则改为自动宽度。指示器在活动标签下方(如果 tabsAtBottom 则在上方)滑动。内容面板一次显示一个页面,带对齐。
完整示例(复制、粘贴并运行)
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());
// 每个标签页一个内容 view(数量必须与标签页数量一致)。
Label resumo = new Label("Conteúdo do Resumo");
Label detalhes = new Label("Conteúdo dos Detalhes");
Label historico = new Label("Conteúdo do Histórico");
TabBar tabs = new TabBar(
new TabBar.Tab("Resumo"),
new TabBar.Tab("Detalhes"),
new TabBar.Tab("Histórico"));
tabs.setContent(resumo, detalhes, historico);
tabs.onTabChanged(i -> System.out.println("aba " + i));
add(LAY_FILL, tabs); // TabBar 吸收剩余的高度
}
}
下面的片段只展示核心部分(放入
onShow()内的内容)。要运行它们,请将它们放入上面的MainWindow骨架中。
基本用法
TabBar tabs = new TabBar(
new TabBar.Tab("Home", new MaterialIcon("")),
new TabBar.Tab("Buscar", new MaterialIcon("")),
new TabBar.Tab("Perfil", new MaterialIcon("")));
tabs.setContent(homeView, searchView, profileView);
tabs.onTabChanged(i -> System.out.println("aba " + i));
add(LAY_FILL, tabs);
构造函数
| 构造函数 | 描述 |
|---|---|
TabBar(Tab... tabs) |
用这些标签创建栏(activeIndex = 0) |
TabBar.Tab(标签描述符)
| 构造函数 | 结果 |
|---|---|
Tab(String text) |
仅文本(TextPos.NONE) |
Tab(Icon icon) |
仅图标(TextPos.NONE) |
Tab(String text, Icon icon) |
文本 + 图标(TextPos.TEXT_AT_BOTTOM) |
Tab(String text, Icon icon, TextPos textPos) |
带自定义位置的文本 + 图标 |
属性与自定义
| 属性 / 方法 | 类型 | 作用 |
|---|---|---|
setContent(Component...) |
TabBar |
各标签的内容(数量必须与标签匹配,否则抛出 IllegalArgumentException) |
setActiveTab(int) |
TabBar |
以编程方式激活一个标签(带动画/滚动);若无效或已激活则为 no-op |
getActiveIndex() |
int |
活动标签的索引 |
getTabs() |
Tab[] |
标签的描述符 |
getStrip() / getTabButtons() / getContentPanel() |
— | 访问内部组件 |
getIndicatorX() / getIndicatorW() |
float |
指示器的当前位置/宽度(用于在 Design 中绘制) |
回调与事件
| 回调 | 何时触发 |
|---|---|
onTabChanged(TabChangedListener) |
切换标签时,通过触摸、滑动或 setActiveTab(...) |
TabChangedListener 是函数式的:void onTabChanged(int index)。
状态
- 活动 —— 一次只有一个标签;指示器动画到它(持续时间
TabBarStyle.switchDuration)。 - 用户滑动 —— 指示器在相邻标签之间连续插值;跨越到另一页面时,
activeIndex切换并触发onTabChanged。 - 编程式 ——
setActiveTab/触摸会锁定同步,直到滚动动画到达目标。
仅文本的标签
TabBar tabs = new TabBar(
new TabBar.Tab("Resumo"),
new TabBar.Tab("Detalhes"),
new TabBar.Tab("Histórico"));
tabs.setContent(resumoView, detalhesView, historicoView);
add(LAY_FILL, tabs);
编程式切换标签
TabBar tabs = new TabBar(new TabBar.Tab("A"), new TabBar.Tab("B"), new TabBar.Tab("C"));
tabs.setContent(viewA, viewB, viewC);
add(LAY_FILL, tabs);
// ... 稍后:
tabs.setActiveTab(2); // 动画切换到标签页 C
最佳实践
setContent(...)的数量必须与标签的数量吻合。- 对于底部标签(iOS 风格),使用一个
TabBarStyle.tabsAtBottom = true的Skin—— 布局和指示器会自行调整。 - 不要强制标签固定宽度:如果它们放不下,strip 会自行滚动。
常见错误
setContent数量错误 →IllegalArgumentException。每个标签正好传入一个 view。- 期待
setActiveTab对已激活的索引触发onTabChanged—— 当index == activeIndex时它是 no-op。
另见
这个页面有帮助吗?感谢你的反馈!