水平滑動條(範圍 0-30,初始 15)控制定時器間隔,驅動 14 幀小狗圖片循環播放。滑動條刻度標籤、主/次刻度、邊框美化。視窗最小化自動暫停,恢復自動繼續。
fps = slider.getValue() // 幀率 0-30 delay = 1000 / fps // 毫秒延遲 timer.setDelay(delay) // 熱更新定時器間隔 fps == 0 → timer.stop() // 0 幀率 = 暫停
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class SliderDemo extends JPanel implements ActionListener, ChangeListener, WindowListener {
private static final int FPS_MIN = 0, FPS_MAX = 30, FPS_INIT = 15;
private static final int FRAMES = 14;
private JLabel pictureLabel;
private Timer timer;
private int frameIndex = 0;
private boolean paused = false;
private ImageIcon[] frames = new ImageIcon[FRAMES];
public SliderDemo() {
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
// 1. 提示標籤
JLabel hint = new JLabel("拖動滑動條調整播放速度", JLabel.CENTER);
hint.setAlignmentX(Component.CENTER_ALIGNMENT);
add(hint);
// 2. 滑動條配置
JSlider slider = new JSlider(JSlider.HORIZONTAL, FPS_MIN, FPS_MAX, FPS_INIT);
slider.addChangeListener(this);
slider.setMajorTickSpacing(10);
slider.setMinorTickSpacing(1);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
add(slider);
// 3. 圖片顯示區
pictureLabel = new JLabel();
pictureLabel.setHorizontalAlignment(JLabel.CENTER);
pictureLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
pictureLabel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLoweredBevelBorder(),
BorderFactory.createEmptyBorder(10, 10, 10, 10)
));
add(pictureLabel);
// 4. 載入幀圖片(此處假設 resources/frames/frame0.gif ... frame13.gif)
for (int i = 0; i < FRAMES; i++) {
frames[i] = createIcon("images/frames/frame" + i + ".gif");
}
updateFrame(0);
// 5. 定時器:初始延遲 1000/15 ≈ 66ms
int delay = 1000 / FPS_INIT;
timer = new Timer(delay, this);
timer.setInitialDelay(delay * 7); // 首輪額外停頓
timer.setCoalesce(true); // 合併延遲事件
timer.start();
}
// --- ChangeListener: 滑動條變化 ---
@Override
public void stateChanged(ChangeEvent e) {
JSlider src = (JSlider) e.getSource();
if (!src.getValueIsAdjusting()) {
int fps = src.getValue();
if (fps == 0) {
if (!paused) stopAnimation();
} else {
int delay = 1000 / fps;
timer.setDelay(delay);
timer.setInitialDelay(delay * 10);
if (paused) startAnimation();
}
}
}
// --- ActionListener: 定時器觸發幀切換 ---
@Override
public void actionPerformed(ActionEvent e) {
frameIndex = (frameIndex + 1) % FRAMES;
updateFrame(frameIndex);
}
private void updateFrame(int idx) {
if (frames[idx] != null) {
pictureLabel.setIcon(frames[idx]);
pictureLabel.setText(null);
} else {
pictureLabel.setIcon(null);
pictureLabel.setText("幀 " + idx + " 缺失");
}
}
private void startAnimation() { timer.start(); paused = false; }
private void stopAnimation() { timer.stop(); paused = true; }
// --- WindowListener: 視窗狀態聯動 ---
@Override public void windowIconified(WindowEvent e) { stopAnimation(); }
@Override public void windowDeiconified(WindowEvent e) { startAnimation(); }
@Override public void windowOpened(WindowEvent e) {}
@Override public void windowClosing(WindowEvent e) {}
@Override public void windowClosed(WindowEvent e) {}
@Override public void windowActivated(WindowEvent e) {}
@Override public void windowDeactivated(WindowEvent e) {}
// 輔助:為頂級視窗註冊 WindowListener
void addWindowListener(Window w) { w.addWindowListener(this); }
protected static ImageIcon createIcon(String path) {
java.net.URL url = SliderDemo.class.getResource(path);
return (url != null) ? new ImageIcon(url) : null;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame("SliderDemo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SliderDemo demo = new SliderDemo();
f.setContentPane(demo);
demo.addWindowListener(f); // 關鍵:註冊到 JFrame
f.pack();
f.setVisible(true);
});
}
}
| 機制 | 實現細節 |
|---|---|
| 滑動條即時性 | getValueIsAdjusting() 過濾拖動中間狀態,僅在釋放鼠標後應用新延遲 |
| 定時器熱更新 | timer.setDelay() 運行中動態修改間隔,無需重建 |
| 幀循環 | 取模運算 (index + 1) % FRAMES 實現無縫循環 |
| 視窗生命周期 | 實現 WindowListener,最小化停止、還原繼續,節省資源 |
| 資源加載 | 構造階段預載所有幀至數組,播放期零 I/O,保證流暢 |
javax.sound.sampled.Clip 配合幀時間戳AnimatedGifEncoder / XugglerSlider + Timeline + ImageView,聲明式關鍵幀動畫