傳統 Swing 實現淡入淡出需繼承元件、重寫 paintComponent、配合 javax.swing.Timer 手動插值,代碼冗長且難維護。本文展示如何結合 SwingX (提供 JXPanel.setAlpha() 一鍵透明度) 與 TimingFramework (提供非線性插值動畫引擎),以極簡代碼實現專業級卡片切換淡入淡出效果。
JPanel,setAlpha(float) 即時影響自身及所有子元件透明度,無需自訂繪製Timer,支援加速/減速曲線、暫停/繼續/反向、多目標屬性同步動畫


import org.jdesktop.animation.timing.Animator;
import org.jdesktop.animation.timing.interpolation.PropertySetter;
import org.jdesktop.swingx.JXPanel;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class FadingCardDemo extends JFrame {
private static final long serialVersionUID = 8005909309849021746L;
private final String CARD_A = "CARD_A";
private final String CARD_B = "CARD_B";
private JPanel cardPanel;
private JXPanel panelA, panelB;
private JButton buttonA, buttonB;
public FadingCardDemo() {
super("Fading Card Demo");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initUI();
}
private void initUI() {
cardPanel = new JPanel(new CardLayout());
// Panel A
buttonA = new JButton("Button A");
buttonA.addActionListener(actionHandler);
panelA = new JXPanel(new BorderLayout());
panelA.add(buttonA, BorderLayout.CENTER);
cardPanel.add(panelA, CARD_A);
// Panel B
buttonB = new JButton("Button B");
buttonB.addActionListener(actionHandler);
panelB = new JXPanel(new BorderLayout());
panelB.add(buttonB, BorderLayout.CENTER);
cardPanel.add(panelB, CARD_B);
Container container = getContentPane();
container.setLayout(new BorderLayout());
container.add(cardPanel, BorderLayout.CENTER);
}
private final ActionListener actionHandler = new ActionListener() {
private Animator animator = null;
@Override
public void actionPerformed(ActionEvent e) {
// 惰性初始化 Animator
if (animator == null) {
animator = new Animator(2000); // 2秒動畫時長
animator.setDeceleration(0.2f); // 減速曲線
animator.setAcceleration(0.4f); // 加速曲線
} else if (animator.isRunning()) {
animator.stop(); // 打斷正在進行的動畫
}
JButton source = (JButton) e.getSource();
if (source == buttonA) {
// 切換到 Panel B 並淡入
animator.addTarget(new PropertySetter(panelB, "alpha", 1.0f));
panelB.setAlpha(0.1f); // 起始透明度
((CardLayout) cardPanel.getLayout()).show(cardPanel, CARD_B);
} else if (source == buttonB) {
// 切換到 Panel A 並淡入
animator.addTarget(new PropertySetter(panelA, "alpha", 1.0f));
panelA.setAlpha(0.1f);
((CardLayout) cardPanel.getLayout()).show(cardPanel, CARD_A);
}
animator.start();
}
};
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
FadingCardDemo demo = new FadingCardDemo();
demo.setSize(new Dimension(400, 300));
demo.setVisible(true);
});
}
}
Animator 可重用,stop() 打斷當前動畫避免疊加setAcceleration/Deceleration 模擬真實物理慣性,體驗更自然PropertySetter(target, "propertyName", endValue) 自動計算插值並反射調用 settershow() 切換可見卡片,再啟動淡入動畫 (因 CardLayout 只顯示一張卡)swingx-all.jar)timingframework.jar)將 JAR 加入類別路徑即可編譯執行。此模式易擴展至工具提示淡現、對話框彈出、狀態欄滑入等多種 UI 動畫場景。