タブにボタンが - JTabbedPane
やっぱりクローズしたい
最近はよくタブで切り替えるアプリケーションがありますね。そうなると、Java でもタブを使いたいわけです。
Swing でタブを使うには JTabbedPane クラスを使います。
でも、もうちょっとなんです。
何がもうちょっとかというと、タブにボタンが貼れないからです。
たとえば、Eclipse ではタブでエディターを切り替えますが、そのタブにはクローズボタンがついています。SWT でできるのに、Swing でできないなんて癪じゃないですか。
でも、Java SE 6 を使えば、任意のコンポーネントをタブに貼れるのです。
とりあえず貼ってみる
タブにコンポーネントを貼るためのメソッドは setTabComponentAt メソッドです。第一引数がタブのインデックス、第 2 引数が貼りつけるコンポーネントです。
| サンプルのソースコード | TabbedPaneSample1.java |
|---|
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTabbedPane;
import javax.swing.UIManager;
public class TabbedPaneSample1 {
public TabbedPaneSample1() {
JFrame frame = new JFrame("TabbedPane Sample");
JTabbedPane pane = new JTabbedPane();
for (int i = 0; i < 5; i++) {
pane.addTab(null, new JLabel("Tab " + i));
pane.setTabComponentAt(i, new JLabel("Tab " + i,
UIManager.getIcon("FileView.fileIcon"),
JLabel.TRAILING));
}
frame.add(pane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
frame.setVisible(true);
}
public static void main(String[] args) {
new TabbedPaneSample1();
}
}
UIManager.getIcon メソッドはデフォルトで用意されているアイコンをロードするためのメソッドで、FileView.fileIcon はファイルのアイコンを示しています。
さて、実行してみましょう。
ここではタブに JLabel オブジェクトを貼っていますが、実をいうとこれは今までの JTabbedPane クラスと変わりません。
JTabbedPane#addTab(String title, Icon icon, Component component) メソッドを使用した場合、内部的に JLabel オブジェクトが生成されて、タブに表示されていたのです。
この内部的に生成されていた JLabel オブジェクトの代わりに任意のコンポーネント (とはいっても Swing のコンポーネントですが) が貼れるようになったわけですね。
でも、なんで addTab(JComponent tabComponent, Component component) みたいなメソッドを作らなかったのでしょう。今の形式だとちょっと使い勝手が悪いと思うは私だけ?
せっかくだからクローズ
コンポーネントが貼れるようになったらやっぱりやりたいのは、タブにクローズボタンをつけることでしょう。
問題はタブに貼ったコンポーネントからイベントが取りだせるかどうかです。
| サンプルのソースコード | TabbedPaneSample2.java |
|---|
まずは JTabbedPane オブジェクトの生成とそこに貼る JLabel オブジェクトを作る部分です。
public TabbedPaneSample2() {
JFrame frame = new JFrame("TabbedPane Sample");
pane = new JTabbedPane();
for (int i = 0; i < 5; i++) {
JLabel label = new JLabel("Tab " + i);
pane.addTab(null, label);
JComponent tabComp = createTabComponent("Tab " + i);
pane.setTabComponentAt(i, tabComp);
}
frame.add(pane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
frame.setVisible(true);
}
タブに貼るコンポーネントは createTabComponent メソッドで作っています。
private JComponent createTabComponent(String title) {
JComponent comp = new JComponent() {};
comp.setLayout(new BorderLayout(5, 5));
JLabel label = new JLabel(title, JLabel.LEFT);
comp.add(label, BorderLayout.CENTER);
ImageIcon icon = new ImageIcon("close.gif");
JButton button = new JButton(icon);
int width = icon.getIconWidth();
int height = icon.getIconHeight();
button.setPreferredSize(new Dimension(width, height));
comp.add(button, BorderLayout.EAST);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
Component tabComp = ((Component)event.getSource()).getParent();
int index = pane.indexOfTabComponent(tabComp);
pane.remove(index);
}
});
return comp;
}
JComponent オブジェクトを作成して、そこにラベルとボタンを配置しています。
ボタンはそのままだとかなり大きく表示されてしまうので、イメージの大きさと同じになるようにしました。
ボタンが押されたときに、何番目のタブか知るために JTabbedPane#indexOfTabCompopnent メソッドを使っています。このメソッドの引数はタブに貼っているコンポーネントなので、JButton オブジェクトを引数にすると例外が発生してしまいます。
そこで、JComponent#getParent メソッドを使って、タブに貼っている JComponent オブジェクトを取り出し、それを indexOfTabComponent メソッドの引数にしています。
インデックスが取得できたら、後は JTabbedPane#remove メソッドを使用して、タブを削除します。
実行すると下図のように表示されます。
Tab 1 のクローズボタンをクリックすると、
ちゃんと削除することができました。
ということはタブに貼ったコンポーネントからもちゃんとイベントを受けとれるということですね。
おわりに
タブにクローズボタンが貼れるようになったのはいいのですが、なんかものたりないと思いませんか。そうです、一番右側のところにもコンポーネントを貼りたいのです。
Eclipse だとこうなっています。
この赤丸のところができればいいのですが。
JTabbedPane クラスを派生させたクラスを作って、ついでに BasicTabbedPaneUI クラスも派生させたクラスを作って、ごりごり書けばできるはずです。
すくなくとも Swing のよさは、自前ですべて描画処理をおこなっているので、ユーザが書きかえようと思えばいくらでもできるところにあります。
興味がある人はソースを解析して、やってみてはいかがですか。勉強になりますよ。
(Nov. 2005)
