---
category: swing
folder: TabOrScrollButtonHighlightAnimation
title: JTabbedPaneのタブ追加位置をハイライト表示する
title-en: Highlighting the location where a tab is added to a JTabbedPane
tags: [JTabbedPane, JLayer, Animatioin]
author: aterai
pubdate: 2025-11-03T05:15:42+09:00
description: JTabbedPaneのタブ追加時にそのタブ領域、領域が非表示の場合はスクロールボタンをハイライトするアニメーションで追加位置を知らせるよう設定します。
summary-jp: JTabbedPaneのタブ追加時にそのタブ領域、領域が非表示の場合はスクロールボタンをハイライトするアニメーションで追加位置を知らせるよう設定します。
summary-en: When adding a tab to a JTabbedPane, the added position will be indicated by an animation that highlights the tab area, or if the area is hidden, the scroll button.
image: https://drive.google.com/uc?id=1TgONMnx7Ui87bY2DEa8uVA6lClmVIidO
---
* Summary [#summary]
JTabbedPaneのタブ追加時にそのタブ領域、領域が非表示の場合はスクロールボタンをハイライトするアニメーションで追加位置を知らせるよう設定します。
`JTabbedPane`のタブ追加時にそのタブ領域、領域が非表示の場合はスクロールボタンをハイライトするアニメーションで追加位置を知らせるよう設定します。
#download(https://drive.google.com/uc?id=1TgONMnx7Ui87bY2DEa8uVA6lClmVIidO)
* Source Code Examples [#sourcecode]
#code(link){{
class TabHighlightLayerUI extends LayerUI<JTabbedPane> {
private static final int MAX = 32;
private final Rectangle rect = new Rectangle();
private final Timer animator = new Timer(10, null);
private transient ActionListener listener;
private int alpha;
@Override public void installUI(JComponent c) {
super.installUI(c);
if (c instanceof JLayer) {
((JLayer<?>) c).setLayerEventMask(AWTEvent.HIERARCHY_EVENT_MASK);
}
}
@Override public void uninstallUI(JComponent c) {
if (c instanceof JLayer) {
((JLayer<?>) c).setLayerEventMask(0);
}
super.uninstallUI(c);
}
@Override public void paint(Graphics g, JComponent c) {
super.paint(g, c);
if (c instanceof JLayer) {
Graphics2D g2 = (Graphics2D) g.create();
float a = alpha / 100f;
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, a));
g2.setPaint(Color.RED);
g2.fill(rect);
g2.dispose();
}
}
@Override protected void processHierarchyEvent(
HierarchyEvent e, JLayer<? extends JTabbedPane> l) {
super.processHierarchyEvent(e, l);
Container parent = e.getChangedParent();
JTabbedPane tabs = l.getView();
long flags = e.getChangeFlags();
if (Objects.equals(parent, tabs) && flags == HierarchyEvent.PARENT_CHANGED) {
EventQueue.invokeLater(() -> startAnime(l, e.getComponent()));
}
}
private void startAnime(JLayer<? extends JTabbedPane> l, Component c) {
JTabbedPane tabs = l.getView();
int idx = tabs.indexOfComponent(c);
Rectangle tabRect = tabs.getBoundsAt(idx);
if (tabs.getBounds().contains(tabRect)) {
rect.setBounds(tabRect);
} else {
JButton b = getScrollForwardButton(tabs);
if (b != null) {
rect.setBounds(b.getBounds());
}
}
animator.start();
animator.removeActionListener(listener);
listener = ae -> {
if (alpha < MAX) {
alpha += 1;
} else {
alpha = 0;
animator.stop();
}
l.paintImmediately(rect);
};
animator.addActionListener(listener);
animator.start();
}
private static JButton getScrollForwardButton(JTabbedPane tabs) {
JButton button1 = null;
JButton button2 = null;
for (Component c : tabs.getComponents()) {
if (c instanceof JButton) {
if (Objects.isNull(button1)) {
button1 = (JButton) c;
} else if (Objects.isNull(button2)) {
button2 = (JButton) c;
}
}
}
int x1 = Objects.nonNull(button1) ? button1.getX() : -1;
int x2 = Objects.nonNull(button2) ? button2.getX() : -1;
return x1 > x2 ? button1 : button2;
}
}
}}
* Description [#description]
- `JTabbedPane.SCROLL_TAB_LAYOUT`を設定した`JTabbedPane`をラップする`JLayer`を設定
- `LayerUI#installUI(...)`をオーバーライドして`JLayer#setLayerEventMask(AWTEvent.HIERARCHY_EVENT_MASK)`で階層イベントを取得するよう設定
- `LayerUI#processHierarchyEvent(...)`をオーバーライドして`JLayer`を設定した`JTabbedPane`の子としてタブコンテンツコンポーネントが追加された後にハイライトアニメーションを開始するよう設定
- `JTabbedPane#indexOfComponent(...)`で追加されたタブのインデックスを取得し、`JTabbedPane#getBoundsAt(...)`でその領域を取得
-- 取得したタブ領域が`JTabbedPane`領域内に含まれる場合はそのタブ領域をハイライトする
-- 取得したタブ領域が`JTabbedPane`領域内に含まれない場合はスクロールボタンの領域をハイライトする
--- このサンプルではタブは末尾にのみ追加するので、`scrollTabsBackward`ボタンより右側に配置された`scrollTabsForward`ボタンをハイライトする
- `Timer`を使用してアルファ値を変更しつつ、`JLayer#paintImmediately(Rectangle)`でハイライト領域を再描画することでアニメーションを行う
* Reference [#reference]
- [[JTabbedPaneのタブをドラッグ&ドロップ>Swing/DnDTabbedPane]]
* Comment [#comment]
#comment
#comment