You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带动画的JPanel缩放:鼠标进出平滑高度切换异常问题

解决JPanel动画过程中鼠标切换导致的异常问题

问题根源

当前代码每次触发鼠标移入/移出事件时,都会新建一个Timer执行动画,但没有终止之前正在运行的Timer。当动画未完成时切换鼠标状态,多个Timer会同时修改面板高度,互相干扰,导致动画逻辑混乱。

修复方案

  1. 将Timer改为全局可控制的实例变量,确保同一时间只有一个动画在运行
  2. 触发新动画前先停止当前的Timer
  3. 动画起始高度使用面板的当前实际高度,而非固定的初始值或目标值,保证中途切换时动画平滑衔接

修复后的完整代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 21:32:25