---
category: swing
folder: TabAreaLayout
title: JTabbedPaneのタブエリアレイアウトを変更して一覧表示ボタンなどを追加する
tags: [JTabbedPane, CardLayout, OverlayLayout, FlowLayout, JPopupMenu]
author: aterai
pubdate: 2022-01-10T02:47:34+09:00
description: JTabbedPaneのタブエリアレイアウトを変更して余白や右端にタブの一覧表示ボタンなどを追加します。
image: https://drive.google.com/uc?id=1J3-SXZfSQEEuctw_T-zb8dsSiSDeoIeJ
---
* 概要 [#summary]
`JTabbedPane`のタブエリアレイアウトを変更して余白や右端にタブの一覧表示ボタンなどを追加します。

#download(https://drive.google.com/uc?id=1J3-SXZfSQEEuctw_T-zb8dsSiSDeoIeJ)

* サンプルコード [#sourcecode]
#code(link){{
protected CardLayoutTabbedPane() {
  super(new BorderLayout());
  setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
  setBackground(new Color(16, 16, 16));
  tabPanel.setInheritsPopupMenu(true);
  hiddenTabs.addActionListener(e -> {
    JButton b = (JButton) e.getSource();
    Point p = b.getLocation();
    JPopupMenu popup = new JPopupMenu();
    for (int i = 0; i < tabPanel.getComponentCount(); i++) {
      JMenuItem mi = makeRadioMenuItem(tabPanel, i);
      if (mi != null) {
        popup.add(mi);
      }
    }
    p.x += b.getWidth() - popup.getPreferredSize().width - 1;
    p.y += b.getHeight();
    popup.show(b.getParent(), p.x, p.y);
  });
  JPanel buttons = new JPanel(new GridBagLayout());
  buttons.add(hiddenTabs);
  JPanel header = new JPanel(new BorderLayout());
  header.add(new JLayer<>(tabArea, new HorizontalScrollLayerUI()));
  header.add(buttons, BorderLayout.EAST);
  add(header, BorderLayout.NORTH);
  add(contentsPanel);
}

private JMenuItem makeRadioMenuItem(Container c, int i) {
  JToggleButton tab = (JToggleButton) c.getComponent(i);
  JViewport vp = (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, c);
  Rectangle rect = SwingUtilities.convertRectangle(c, tab.getBounds(), vp);
  if (vp.getBounds().contains(rect)) {
  JViewport viewport = tabArea.getViewport();
  Rectangle r = tab.getBounds();
  if (viewport.getViewRect().contains(r)) {
    return null;
  }
  JLabel l = (JLabel) tab.getComponent(0);
  String title = l.getText();
  JLabel label = (JLabel) tab.getComponent(0);
  String title = label.getText();
  JMenuItem mi = new JRadioButtonMenuItem(title);
  mi.addActionListener(e -> {
    tab.setSelected(true);
    cardLayout.show(contentsPanel, title);
    tab.scrollRectToVisible(rect);
    viewport.scrollRectToVisible(SwingUtilities.convertRectangle(c, r, viewport));
  });
  return mi;
}
}}

* 解説 [#explanation]
- 上: `JTabbedPane` + `Box` + `OverlayLayout`
-- タブエリアの右端に余白を設定した`JTabbedPane`と、右端に`JButton`を配置した透明な`Box`を`OverlayLayout`で同じサイズで重ねて表示
--- [[JTabbedPaneの余白にJButtonを配置>Swing/TabbedPaneWithButton]]の余白と配置を右端に変更
-- 右端に追加した`JButton`をクリックするとタブ選択状態を変更可能な`JPopupMenu`が開く
-- `JTabbedPane`のタブレイアウトポリシーが`SCROLL_TAB_LAYOUT`の場合、`UIManager.put("TabbedPane.tabAreaInsets", new Insets(...))`などで設定した余白はスクロールボタンなどで無視される仕様?ため、このサンプルのポップアップボタンと重なってしまう
--- `SCROLL_TAB_LAYOUT`で使用する場合はこの仕様を回避するより`Box`を不透明にして各タブを表示する`JScrollPane`を追加するほうが簡単かもしれない
- 下: `JPanel` + `CardLayout` + `JScrollPane` + `FlowLayout` + `BorderLayout`
-- `CardLayout`で`JTabbedPane`風のコンポーネントを作成し、そのタブエリアの中央に`JScrollPane`と右端に`JButton`を追加
--- [[CardLayoutで作成したJTabbedPane風コンポーネントのタブエリアに水平JScrollBarを表示する>Swing/TabAreaScrollBar]]
-- 右端に追加した`JButton`をクリックすると`JViewport`外のタブを選択し`scrollRectToVisible(...)`メソッドを実行してスクロール表示可能な`JPopupMenu`が開く

* 参考リンク [#reference]
- [[JTabbedPaneの余白にJButtonを配置>Swing/TabbedPaneWithButton]]
- [[CardLayoutで作成したJTabbedPane風コンポーネントのタブエリアに水平JScrollBarを表示する>Swing/TabAreaScrollBar]]

* コメント [#comment]
#comment
#comment