Search code examples
javaswingjpanel

Resize JPanel with animation


I want to make a panel that, when the mouse enters it, its height increases smoothly and when the mouse leaves it, its height decreases smoothly.

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;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);

            JPanel panel = new JPanel();
            panel.setPreferredSize(new Dimension(200, INITIAL_HEIGHT));
            panel.setBackground(Color.BLUE);

            frame.add(panel);
            frame.pack();

            panel.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseEntered(MouseEvent e) {
                    new Timer(ANIMATION_DELAY, new ActionListener() {
                        private int currentHeight = INITIAL_HEIGHT;

                        @Override
                        public void actionPerformed(ActionEvent e) {
                            if (currentHeight < TARGET_HEIGHT) {
                                currentHeight += INCREMENT;
                                panel.setPreferredSize(new Dimension(200, currentHeight));
                                panel.revalidate();
                                frame.pack();
                            } else {
                                ((Timer) e.getSource()).stop();
                            }
                        }
                    }).start();
                }
                @Override
                public void mouseExited(MouseEvent e) {
                    new Timer(ANIMATION_DELAY, new ActionListener() {
                        private int currentHeight = TARGET_HEIGHT;

                        @Override
                        public void actionPerformed(ActionEvent e) {
                            if (currentHeight > INITIAL_HEIGHT) {
                                currentHeight -= INCREMENT;
                                panel.setPreferredSize(new Dimension(200, currentHeight));
                                panel.revalidate();
                                frame.pack();
                            } else {
                                ((Timer) e.getSource()).stop();
                            }
                        }
                    }).start();
                }
            });
        });
    }
}

The problem is that if I move the mouse out of the panel while changing the size of the panel, it does not work properly.


Solution

  • Try to use only one timer and add a boolean to track the direction and make sure that the animation is happening in one direction

    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;
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationRelativeTo(null);
            frame.setSize(300, 300); // Adjust frame size to better see the effect
    
            JPanel panel = new JPanel();
            panel.setPreferredSize(new Dimension(200, INITIAL_HEIGHT));
            panel.setBackground(Color.BLUE);
    
            frame.add(panel);
            frame.pack();
            frame.setVisible(true);
    
            Timer timer = new Timer(ANIMATION_DELAY, null);
            boolean growing = false;
    
            panel.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseEntered(MouseEvent e) {
                    if (!growing) {
                        growing = true;
                        timer.removeActionListener(timer.getActionListeners()[0]);
                        timer.addActionListener(new ActionListener() {
                            private int currentHeight = panel.getPreferredSize().height;
    
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                if (currentHeight < TARGET_HEIGHT) {
                                    currentHeight += INCREMENT;
                                    panel.setPreferredSize(new Dimension(200, currentHeight));
                                    panel.revalidate();
                                    frame.pack();
                                } else {
                                    timer.stop();
                                }
                            }
                        });
                        timer.start();
                    }
                }
    
                @Override
                public void mouseExited(MouseEvent e) {
                    if (growing) {
                        growing = false;
                        timer.removeActionListener(timer.getActionListeners()[0]);
                        timer.addActionListener(new ActionListener() {
                            private int currentHeight = panel.getPreferredSize().height;
    
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                if (currentHeight > INITIAL_HEIGHT) {
                                    currentHeight -= INCREMENT;
                                    panel.setPreferredSize(new Dimension(200, currentHeight));
                                    panel.revalidate();
                                    frame.pack();
                                } else {
                                    timer.stop();
                                }
                            }
                        });
                        timer.start();
                    }
                }
            });
        });
    }}