跳到内容
CrossFly 是 100% 开源在 GitHub 关注我们
CrossFlyCrossFly
开始使用

本页面由葡萄牙语机器翻译,可能存在错误。葡萄牙语版本为准。

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。

另见

这个页面有帮助吗?