Swing/DotNavigationSlideshow のバックアップ(No.1)
- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- Swing/DotNavigationSlideshow へ行く。
- 1 (2026-03-16 (月) 09:18:21)
- category: swing folder: DotNavigationSlideshow title: JToggleButtonでドットインジケーターを作成する title-en: Create a dot indicator with JToggleButton tags: [JToggleButton, JLayeredPane, Animation] author: aterai pubdate: 2026-03-16T00:22:39+09:00 description: JToggleButtonでドットインジケーターを作成してページ下部に配置し、マウスクリックなどでスクロールアニメーション付きの画面遷移を実行します。 summary-jp: JToggleButtonでドットインジケーターを作成してページ下部に配置し、マウスクリックなどでスクロールアニメーション付きの画面遷移を実行します。 summary-en: Create a dot indicator with JToggleButton and place it at the bottom of the page, and perform a screen transition with a scroll animation when the mouse is clicked, etc. image: https://drive.google.com/uc?id=1_XbkqoUbbWYn3wFnlL2NUGlB8h0-M_eF
Summary
JToggleButtonでドットインジケーターを作成してページ下部に配置し、マウスクリックなどでスクロールアニメーション付きの画面遷移を実行します。
Screenshot

Advertisement
Source Code Examples
private void setupDots() {
int totalImages = images.size();
ButtonGroup group = new ButtonGroup();
for (int i = 0; i < totalImages; i++) {
JToggleButton dot = makeDotButton(i);
group.add(dot);
dotPanel.add(dot);
}
}
private JToggleButton makeDotButton(int index) {
JToggleButton dot = new JToggleButton(new DotIcon(), index == 0);
dot.setBorderPainted(false);
dot.setContentAreaFilled(false);
dot.setFocusPainted(false);
dot.setBorder(BorderFactory.createEmptyBorder());
dot.setCursor(new Cursor(Cursor.HAND_CURSOR));
dot.addActionListener(e -> {
if (index != currentIndex) {
navigateTo(index, index > currentIndex);
}
});
return dot;
}
private void navigateTo(int index, boolean moveRight) {
Component c = dotPanel.getComponent(index);
if (c instanceof JToggleButton && !animationPanel.isAnimating()) {
((JToggleButton) c).setSelected(true);
ImageIcon nextImage = images.get(index);
currentIndex = index;
animationPanel.startAnimation(nextImage, moveRight);
requestFocusInWindow();
}
}
private class NavigateHandler extends KeyAdapter
implements MouseWheelListener {
@Override public void keyPressed(KeyEvent e) {
int totalImages = images.size();
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_LEFT) {
navigateTo((currentIndex - 1 + totalImages) % totalImages, false);
} else if (keyCode == KeyEvent.VK_RIGHT) {
navigateTo((currentIndex + 1) % totalImages, true);
}
}
@Override public void mouseWheelMoved(MouseWheelEvent e) {
int totalImages = images.size();
int rotation = e.getWheelRotation();
if (rotation < 0) {
navigateTo((currentIndex - 1 + totalImages) % totalImages, false);
} else {
navigateTo((currentIndex + 1) % totalImages, true);
}
}
}
View in GitHub: Java, KotlinDescription
- スライドショーを
JLayeredPaneにOverlayLayoutを設定して作成:- 背面レイヤー(
JLayeredPane.DEFAULT_LAYER)にアニメーションパネルを追加 - 前面レイヤー(
JLayeredPane.PALETTE_LAYER)にドットナビゲーションパネルや前後移動ボタンを配置した透明パネルを追加
- 背面レイヤー(
- 前面レイヤーを
JPanelにBorderLayoutを設定して作成:JToggleButtonで作成したドットをスライドショーに表示する画像の数だけ作成して透明なJPanelにFlowLayout.CENTERで配置、さらにこのドットナビゲーションパネルをラップするJPanel(BorderLayout)のBorderLayout.SOUTHに配置、最後に前面レイヤーJPanelのBorderLayout.CENTERに配置- 通常の
BorderLayoutではBorderLayout.SOUTHに配置したコンポーネントの幅がBorderLayout.WEST、BorderLayout.EASTに配置したコンポーネントの高さより優先されるので、これを逆にするためドットナビゲーションパネルを別JPanel(BorderLayout)でラップしている KeyListenerやMouseWheelListenerでの画面遷移は前後移動のみだが、ドットボタンのクリックは間の画面を飛ばして目的の画面にスライドアニメーションで移動可能
- 通常の
JButtonで作成した前後移動ボタンをBorderLayout.WEST、BorderLayout.EASTに配置
- 背面レイヤーを
EaseOutを用いた画像スライドアニメーションを処理するJPanelで作成:
class AnimationPanel extends JPanel {
private static final int DURATION = 250; // ms
private static final int FRAME_RATE = 60;
private final Timer timer;
private Image currentImage;
private Image nextImage;
private double animationProgress; // 0.0 to 1.0
private boolean moveRight = true;
public AnimationPanel(ImageIcon initialImage) {
super();
this.currentImage = initialImage.getImage();
int delay = 1000 / FRAME_RATE;
timer = new Timer(delay, new ActionListener() {
private long startTime;
@Override public void actionPerformed(ActionEvent e) {
boolean b0 = animationProgress == 0d;
if (b0) {
startTime = System.currentTimeMillis();
animationProgress = .001; // Start
} else {
long elapsed = System.currentTimeMillis() - startTime;
animationProgress = (double) elapsed / DURATION;
boolean b1 = animationProgress >= 1d;
if (b1) {
animationProgress = 1d;
completeAnimation();
}
}
repaint();
}
});
timer.setInitialDelay(0);
}
public void startAnimation(ImageIcon nextImageIcon, boolean isMoveRight) {
this.nextImage = nextImageIcon.getImage();
this.moveRight = isMoveRight;
this.animationProgress = 0d;
timer.start();
}
private void completeAnimation() {
timer.stop();
currentImage = nextImage; // Swap images
// nextImage = null;
animationProgress = 0d;
}
public boolean isAnimating() {
return timer.isRunning();
}
private double easeOut(double t) {
return t * (2d - t);
}
@Override protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (currentImage == null) {
return;
}
Graphics2D g2 = (Graphics2D) g.create();
int width = getWidth();
int height = getHeight();
if (isAnimating() && nextImage != null) {
double easedProgress = easeOut(animationProgress);
int offsetX = (int) (width * easedProgress);
if (moveRight) {
// Slide to left (new image comes from right)
g2.drawImage(currentImage, -offsetX, 0, width, height, this);
g2.drawImage(nextImage, width - offsetX, 0, width, height, this);
} else {
// Slide to right (new image comes from left)
g2.drawImage(currentImage, offsetX, 0, width, height, this);
g2.drawImage(nextImage, -width + offsetX, 0, width, height, this);
}
} else {
// Static state
g2.drawImage(currentImage, 0, 0, width, height, this);
}
g2.dispose();
}
}