• 追加された行はこの色です。
  • 削除された行はこの色です。
---
category: swing
folder: TabbedPaneWithButton
title: JTabbedPaneの余白にJButtonを配置
tags: [JTabbedPane, OverlayLayout, JButton, UIManager]
author: aterai
pubdate: 2008-03-03T14:07:59+09:00
description: JTabbedPaneのタブエリアに余白を作成し、そこにOverlayLayoutを使ってJButtonを配置します。
image: https://lh5.googleusercontent.com/_9Z4BYR88imo/TQTUOdUT3wI/AAAAAAAAAls/N2JYE_Dcr_Y/s800/TabbedPaneWithButton.png
---
* 概要 [#g997f976]
* 概要 [#summary]
`JTabbedPane`のタブエリアに余白を作成し、そこに`OverlayLayout`を使って`JButton`を配置します。

#download(https://lh5.googleusercontent.com/_9Z4BYR88imo/TQTUOdUT3wI/AAAAAAAAAls/N2JYE_Dcr_Y/s800/TabbedPaneWithButton.png)

* サンプルコード [#l9d78f5a]
* サンプルコード [#sourcecode]
#code(link){{
final JButton b = new ToolBarButton(icon);
JButton b = new ToolBarButton(icon);
b.addActionListener(new ActionListener() {
  @Override public void actionPerformed(ActionEvent e) {
    tabs.addTab("qwerqwer", new JLabel("yetyet"));
  }
});
tabs = new ClippedTitleTabbedPane() {
  @Override public void updateUI() {
    UIManager.put("TabbedPane.tabAreaInsets", null); //uninstall
    UIManager.put("TabbedPane.tabAreaInsets", null); // uninstall
    super.updateUI();
    setAlignmentX(0.0f);
    setAlignmentY(0.0f);
    b.setBorder(BorderFactory.createEmptyBorder(3,3,3,3));
    b.setAlignmentX(0.0f);
    b.setAlignmentY(0.0f);
    setAlignmentX(Component.LEFT_ALIGNMENT);
    setAlignmentY(Component.TOP_ALIGNMENT);
    b.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
    setAlignmentX(Component.LEFT_ALIGNMENT);
    setAlignmentY(Component.TOP_ALIGNMENT);
    tabAreaInsets = getTabAreaInsets();
    UIManager.put("TabbedPane.tabAreaInsets",
                  getButtonPaddingTabAreaInsets(b, getTabInsets(), tabAreaInsets));
    super.updateUI();
  }
  private Insets tabAreaInsets = null;
};
JPanel p = new JPanel();
p.setLayout(new OverlayLayout(p));
p.add(button);
p.add(tabs);
}}
// ...

#code{{
//Insets ti = UIManager.getInsets("TabbedPane.tabInsets");
//Insets ai = UIManager.getInsets("TabbedPane.tabAreaInsets");
// Insets ti = UIManager.getInsets("TabbedPane.tabInsets");
// Insets ai = UIManager.getInsets("TabbedPane.tabAreaInsets");
public Insets getButtonPaddingTabAreaInsets(JButton b, Insets ti, Insets ai) {
  FontMetrics fm = b.getFontMetrics(b.getFont());
  int tih = b.getPreferredSize().height-fm.getHeight()-ti.top-ti.bottom-ai.bottom;
  return new Insets(Math.max(ai.top, tih), b.getPreferredSize().width+ai.left, ai.bottom, ai.right);
  int tih = b.getPreferredSize().height - fm.getHeight() - ti.top - ti.bottom - ai.bottom;
  return new Insets(Math.max(ai.top, tih), b.getPreferredSize().width + ai.left, ai.bottom, ai.right);
}
}}

* 解説 [#f485c989]
上記のサンプルは、タブブラウザ風の動作となるように設定しています。
* 解説 [#explanation]
上記のサンプルは、`JTabbedPane`でタブブラウザ風の動作を実現するために、以下のような設定を行っています。

- タブエリアの左上にあるボタンをクリックするとタブが追加される
- タブエリアの左上にあるボタンをクリックするとタブが追加する
- メニューからすべてのタブを削除する
- タブエリアに余裕がある場合は`80px`、無い場合は(タブエリアの幅/タブ数)と、常にタブ幅は一定
-- 折り返しや、スクロールが発生するとレイアウトが崩れるため
- タブエリアに余裕がある場合は`80px`、無い場合は(タブエリアの幅/タブ数)と常にタブ幅は一定
-- 折り返しやスクロールが発生するとレイアウトが崩れることを防ぐための設定

コンポーネントの追加には、以下の方法を使用しています(比較:[[JTabbedPaneの余白にJCheckBoxを配置>Swing/TabbedPaneWithCheckBox]])。
- 以下の手順でコンポーネントの追加を実行
-- [[JTabbedPaneの余白にJCheckBoxを配置>Swing/TabbedPaneWithCheckBox]]
- ボタンの幅だけ`tabAreaInsets`の左余白を拡大する
-- `UIManager.getInsets("TabbedPane.tabAreaInsets")`などを使用するため`Synth`など(`GTK`, `Nimbus`)の`LookAndFeel`には対応していない
-- [https://bugs.openjdk.org/browse/JDK-6634504 Nimbus L&F: java.lang.NullPointer Exception throws when extended BaseUI Components]
- `OverlayLayout`で`JButton`と`JTabbedPane`(上で作った余白に)を重ねて表示
-- このため`JTabbedPane.TOP`にしか対応していない

- ボタンの幅だけ、`tabAreaInsets`の左余白を拡大する
-- `UIManager.getInsets("TabbedPane.tabAreaInsets")`などを使用するため、`Synth`など(`GTK`, `Nimbus`)の`LookAndFeel`には対応していない
-- [http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6634504 Nimbus L&F: java.lang.NullPointer Exception throws when extended BaseUI Components]
- `OverlayLayout`で、`JButton`と`JTabbedPane`(上で作った余白に)を重ねて表示
-- このため、`JTabbedPane.TOP`にしか対応していない
----
- `JTabbedPane`の左端ではなく右端に`JButton`を配置するサンプル

* 参考リンク [#j34dcca9]
#code{{
import java.awt.*;
import javax.swing.*;

public class TabbedPaneWithButtonTest {
  public Component makeUI() {
    JTabbedPane tabs = new JTabbedPane();
    tabs.setAlignmentX(Component.RIGHT_ALIGNMENT);
    tabs.setAlignmentY(Component.TOP_ALIGNMENT);
    tabs.addTab("Tab 1", new JLabel("1"));
    tabs.addTab("Tab 2", new JLabel("2"));

    JButton button = new JButton("https://ateraimemo.com/");
    button.setAlignmentX(Component.RIGHT_ALIGNMENT);
    button.setAlignmentY(Component.TOP_ALIGNMENT);

    JPanel p = new JPanel();
    p.setLayout(new OverlayLayout(p));
    p.add(button);
    p.add(tabs);
    return p;
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
      } catch (Exception ex) {
        throw new IllegalArgumentException(ex);
      }
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new TabbedPaneWithButtonTest().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}
}}

* 参考リンク [#reference]
- [http://www.famfamfam.com/lab/icons/mini/ famfamfam.com: Mini Icons]
-- 追加アイコンを拝借しています。
-- アイコンを借用
- [[OverlayLayoutの使用>Swing/OverlayLayout]]
- [[JTabbedPaneの余白にJCheckBoxを配置>Swing/TabbedPaneWithCheckBox]]
- [[JTabbedPaneのタイトルをクリップ>Swing/ClippedTabLabel]]
- [https://forums.oracle.com/thread/1389350 Swing - Any layout suggestions for this?]
- [https://community.oracle.com/thread/1389350 Swing - Any layout suggestions for this?]
- [[JTabbedPaneのタブエリアレイアウトを変更して一覧表示ボタンなどを追加する>Swing/TabAreaLayout]]

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