CardLayoutで作成したJTabbedPane風コンポーネントのタブエリアに水平JScrollBarを表示する
Total: 1409
, Today: 2
, Yesterday: 5
Posted by aterai at
Last-modified:
概要
CardLayout
を使用してJTabbedPane
風のコンポーネントを作成し、そのタブエリアに水平JScrollBar
を表示してスクロール可能にします。
Screenshot
Advertisement
サンプルコード
class CardLayoutTabbedPane extends JPanel {
private final CardLayout cardLayout = new CardLayout();
private final JPanel tabPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
private final JPanel contentsPanel = new JPanel(cardLayout);
private final JButton hiddenTabs = new JButton("V");
private final ButtonGroup group = new ButtonGroup();
private final JScrollPane tabArea = new JScrollPane(tabPanel) {
@Override public boolean isOptimizedDrawingEnabled() {
return false; // JScrollBar is overlap
}
@Override public void updateUI() {
super.updateUI();
EventQueue.invokeLater(() -> {
getVerticalScrollBar().setUI(new OverlappedScrollBarUI());
getHorizontalScrollBar().setUI(new OverlappedScrollBarUI());
setLayout(new OverlapScrollPaneLayout());
setComponentZOrder(getVerticalScrollBar(), 0);
setComponentZOrder(getHorizontalScrollBar(), 1);
setComponentZOrder(getViewport(), 2);
});
setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
getVerticalScrollBar().setOpaque(false);
getHorizontalScrollBar().setOpaque(false);
setBackground(Color.DARK_GRAY);
setViewportBorder(BorderFactory.createEmptyBorder());
setBorder(BorderFactory.createEmptyBorder());
}
@Override public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
d.height = 18 + 6;
return d;
}
};
protected CardLayoutTabbedPane() {
super(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
setBackground(new Color(16, 16, 16));
tabPanel.setInheritsPopupMenu(true);
hiddenTabs.setFont(hiddenTabs.getFont().deriveFont(8f));
hiddenTabs.setBorder(BorderFactory.createEmptyBorder(2, 8, 2, 8));
hiddenTabs.setOpaque(false);
hiddenTabs.setFocusable(false);
hiddenTabs.setContentAreaFilled(false);
JPanel header = new JPanel(new BorderLayout());
header.add(new JLayer<>(tabArea, new HorizontalScrollLayerUI()));
header.add(hiddenTabs, BorderLayout.EAST);
add(header, BorderLayout.NORTH);
add(contentsPanel);
}
protected JComponent createTabComponent(String title, Icon icon) {
JToggleButton tab = new TabButton();
tab.setInheritsPopupMenu(true);
group.add(tab);
tab.addMouseListener(new MouseAdapter() {
@Override public void mousePressed(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
((AbstractButton) e.getComponent()).setSelected(true);
cardLayout.show(contentsPanel, title);
}
}
});
EventQueue.invokeLater(() -> tab.setSelected(true));
JLabel label = new JLabel(title, icon, SwingConstants.LEADING);
label.setForeground(Color.WHITE);
label.setIcon(icon);
label.setOpaque(false);
JButton close = new JButton(new CloseTabIcon(new Color(0xB0_B0_B0))) {
@Override public Dimension getPreferredSize() {
return new Dimension(12, 12);
}
};
close.addActionListener(e -> {
tabPanel.remove(tab);
contentsPanel.remove(comp);
boolean oneOrMore = tabPanel.getComponentCount() > 1;
if (oneOrMore) {
tabPanel.revalidate();
TabButton b = (TabButton) tabPanel.getComponent(0);
b.setSelected(true);
cardLayout.first(contentsPanel);
}
tabPanel.revalidate();
});
close.setBorder(BorderFactory.createEmptyBorder());
close.setFocusable(false);
close.setOpaque(false);
// close.setFocusPainted(false);
close.setContentAreaFilled(false);
close.setPressedIcon(new CloseTabIcon(new Color(0xFE_FE_FE)));
close.setRolloverIcon(new CloseTabIcon(new Color(0xA0_A0_A0)));
tab.add(label);
tab.add(close, BorderLayout.EAST);
return tab;
}
public void addTab(String title, Icon icon, Component comp) {
JComponent tab = createTabComponent(title, icon);
tabPanel.add(tab);
contentsPanel.add(comp, title);
cardLayout.show(contentsPanel, title);
EventQueue.invokeLater(() -> tabPanel.scrollRectToVisible(tab.getBounds()));
}
public JScrollPane getTabArea() {
return tabArea;
}
@Override public void doLayout() {
BoundedRangeModel m = tabArea.getHorizontalScrollBar().getModel();
hiddenTabs.setVisible(m.getMaximum() - m.getExtent() > 0);
super.doLayout();
}
}
View in GitHub: Java, Kotlin解説
CardLayoutTabbedPane
- CardLayoutを使ってJTabbedPane風のコンポーネントを作成と同様のコンポーネントを使用
TabPanel
new FlowLayout(FlowLayout.LEADING, 0, 0)
を設定したJPanel
にJToggleButton
で作成したタブを配置- タブには
JToggleButton
を使用し、JLabel
でタイトルアイコンと文字列、JButton
でクローズボタン(機能は未実装)を追加 - 右クリックでのタブ切り替えは無効、また
JComponent#setInheritsPopupMenu(true)
でTabArea
などに設定したJPopupMenu
が存在すれば右クリックでそれを開くよう設定
TabArea
new JScrollPane(tabPanel)
でTabArea
を作成し、水平JScrollBar
の増減ボタンやトラックを非表示に設定- JComboBoxのドロップダウンリストで使用するJScrollBarを変更すると同様
JTabbedPane
ではTabArea
にJViewport
をオーバーライドしたプライベートなBasicTabbedPaneUI.ScrollableTabViewport
クラスを使用しているので外部からJScrollBar
などを変更しづらい
JLayer
TabArea
にJLayer
を被せて水平JScrollBar
の表示・非表示の切り替え、MouseWheel
によるスクロール機能などを追加- JScrollBar上にマウスカーソルが入ったらその幅を拡張する
Header
JPanel
でHeader
を作成し、TabArea
に水平JScrollBar
が表示される場合は非表示タブ一覧を表示するためのメニューボタンとJLayer
を追加- 隠れているタブが存在しない場合は非表示タブ一覧表示メニューボタンは非表示にする
- 隠れているタブが存在するかどうかは
JScrollPane#doLayout()
メソッドをオーバーライドし、水平JScrollBar
のノブが表示されているかどうかで判断する - 隠れているタブの一覧表示機能は未実装
参考リンク
- CardLayoutを使ってJTabbedPane風のコンポーネントを作成
- JComboBoxのドロップダウンリストで使用するJScrollBarを変更する
- JScrollBar上にマウスカーソルが入ったらその幅を拡張する
- JTabbedPaneのタブエリアレイアウトを変更して一覧表示ボタンなどを追加する