带动画的JPanel缩放:鼠标进出平滑高度切换异常问题
解决JPanel动画过程中鼠标切换导致的异常问题
问题根源
当前代码每次触发鼠标移入/移出事件时,都会新建一个Timer执行动画,但没有终止之前正在运行的Timer。当动画未完成时切换鼠标状态,多个Timer会同时修改面板高度,互相干扰,导致动画逻辑混乱。
修复方案
- 将
Timer改为全局可控制的实例变量,确保同一时间只有一个动画在运行 - 触发新动画前先停止当前的
Timer - 动画起始高度使用面板的当前实际高度,而非固定的初始值或目标值,保证中途切换时动画平滑衔接
修复后的完整代码
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Main { private static final int INITIAL_HEIGHT = 150; private static final int TARGET_HEIGHT = 200; private static final int ANIMATION_DELAY = 10; private static final int INCREMENT = 2; // 全局控制的Timer实例 private static Timer animationTimer; public static void main(String[] args) { SwingUtilities.invokeLater(() -> { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLocationRelativeTo(null); JPanel panel = new JPanel(); panel.setPreferredSize(new Dimension(200, INITIAL_HEIGHT)); panel.setBackground(Color.BLUE); frame.add(panel); frame.pack(); frame.setVisible(true); panel.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { // 停止当前运行的动画 if (animationTimer != null && animationTimer.isRunning()) { animationTimer.stop(); } animationTimer = new Timer(ANIMATION_DELAY, new ActionListener() { // 以面板当前高度作为动画起始值 private int currentHeight = panel.getPreferredSize().height; @Override public void actionPerformed(ActionEvent e) { if (currentHeight < TARGET_HEIGHT) { currentHeight += INCREMENT; // 防止高度超出目标值 if (currentHeight > TARGET_HEIGHT) { currentHeight = TARGET_HEIGHT; } panel.setPreferredSize(new Dimension(200, currentHeight)); panel.revalidate(); frame.pack(); } else { ((Timer) e.getSource()).stop(); } } }); animationTimer.start(); } @Override public void mouseExited(MouseEvent e) { // 停止当前运行的动画 if (animationTimer != null && animationTimer.isRunning()) { animationTimer.stop(); } animationTimer = new Timer(ANIMATION_DELAY, new ActionListener() { // 以面板当前高度作为动画起始值 private int currentHeight = panel.getPreferredSize().height; @Override public void actionPerformed(ActionEvent e) { if (currentHeight > INITIAL_HEIGHT) { currentHeight -= INCREMENT; // 防止高度低于初始值 if (currentHeight < INITIAL_HEIGHT) { currentHeight = INITIAL_HEIGHT; } panel.setPreferredSize(new Dimension(200, currentHeight)); panel.revalidate(); frame.pack(); } else { ((Timer) e.getSource()).stop(); } } }); animationTimer.start(); } }); }); } }
额外优化点
- 添加边界值判断,避免高度超出
INITIAL_HEIGHT或TARGET_HEIGHT范围 - 调整
frame.setVisible(true)的调用顺序,符合Swing界面初始化的最佳实践
内容的提问来源于stack exchange,提问作者BaRiBoD
相关产品推荐
相关产品推荐

